skip to content

Mockito rejects some stubbed exceptions with the message "Checked exception is invalid for this method". What rule is it enforcing, and what are your options when you hit it?

level: middleimportance: should knowfreq 40%

answer

  1. Unchecked always; checked only if declared in throws
  2. Mock must stay inside the type's contract
  3. Wrap: UncheckedIOException / domain exception
  4. thenThrow(Class) uses the no-arg constructor
  5. Answer sneaky-throw bypasses it - not a fix

basics

~20 s

A stubbed checked exception must be declared in the stubbed method's throws clause (or be a subtype of a declared one). Unchecked exceptions are always allowed. Options: throw a declared or unchecked exception, or change the interface if the failure is real.

solid answer

~50 s

Mockito validates `thenThrow` against the method signature: an exception is acceptable if it is a `RuntimeException`, an `Error`, or a checked exception assignable to something in the method's `throws` clause. Otherwise you get `MockitoException: Checked exception is invalid for this method`. The rule exists because a mock must behave like a legal implementation of the type - real code could never throw an undeclared checked exception there, so a test that pretends otherwise is testing an impossible world. Options when you hit it: 1. **Check the design first.** If the collaborator can genuinely fail that way, the `throws` clause is wrong; fix the interface. 2. **Throw what the contract allows** - the declared checked exception, or an unchecked wrapper such as `UncheckedIOException`, which is often what the production adapter really throws. 3. **Use a broader declared supertype** if the method declares `throws Exception`. A custom `Answer` that throws bypasses the check via sneaky-throw, but it produces behaviour the compiler says is impossible - avoid it as a workaround.

code

java · 10 lines
java
interface Reader { String read(); }              // declares nothing

when(reader.read()).thenThrow(new IOException()); // MockitoException: checked exception is invalid

// Fix A: throw what production really surfaces
when(reader.read()).thenThrow(new UncheckedIOException(new IOException("disk")));

// Fix B: the failure is part of the contract - declare it
interface Reader2 { String read() throws IOException; }
when(reader2.read()).thenThrow(new IOException("disk")); // now legal

go deeper

for a junior

Say that the exception must be one the method declares, or an unchecked one, and show the unchecked alternative.

for a middle

State the rule precisely - including subtypes of declared exceptions - and give the wrapping fix with a concrete unchecked exception.

for a senior

Treat the error as a design signal: decide whether the contract should declare the failure or whether production wraps it, and reject the sneaky-throw workaround with reasons.

for a principal

Talk about checked exceptions at module boundaries generally - which failures belong in a signature, which get wrapped - and how that policy keeps test doubles honest.

## The rule When you write `when(mock.read()).thenThrow(new IOException())`, Mockito inspects the stubbed method's signature and asks: could a legal implementation of this method throw this? The answer is yes if the exception is - a `RuntimeException` or one of its subclasses, or - an `Error` or one of its subclasses, or - a checked exception that is assignable to one of the types in the method's `throws` clause. If none holds, Mockito raises `org.mockito.exceptions.base.MockitoException` with "Checked exception is invalid for this method". The same validation applies to `thenThrow(SomeException.class)`, and to the `doThrow` family. ## Why the rule exists A mock stands in for a real implementation of a type. Java's checked-exception rules mean no real implementation of `String read()` can throw `IOException` - the compiler would reject it. If Mockito allowed the stub anyway, the test would exercise a path that cannot occur in production, and the surrounding code would not even be able to `catch (IOException e)` without a compile error. Mockito refuses to build that fiction. It is the same philosophy that has mocks return type-correct defaults: the double should stay inside the type's contract. ## Diagnosing it The message names the method and the exception. Nearly always one of these is true: 1. **You stubbed the wrong overload or the wrong method.** Its signature declares nothing, or declares something narrower than you assumed. 2. **The interface is an abstraction over a checked-exception-throwing implementation** - a repository interface with no `throws` over JDBC code that throws `SQLException`. The production adapter must already wrap it, so wrapping in the test is faithful, not a workaround. 3. **The design is wrong**: the collaborator really can fail this way and the interface hides it. Then adding the exception to the `throws` clause is the fix, and the test was doing its job by exposing the gap. ## Options in order of preference **Throw what the contract allows.** If the interface declares `throws PaymentException`, throw that or a subtype. If it declares nothing, throw the unchecked exception the production code actually surfaces - `UncheckedIOException`, `DataAccessException`, a domain `PaymentFailedException`. This usually improves the test, because it forces you to state what the caller will really see. **Fix the signature.** If the failure is genuinely part of the contract, declare it. In a pre-production codebase this is cheap and it removes the discrepancy at the source instead of papering over it. **Widen the match.** If the method declares `throws Exception` (common in legacy interfaces and in `Callable`), any checked exception is legal and the error disappears - though a broad `throws Exception` is its own smell. ## The workaround to know about but not reach for A custom `Answer` may throw anything, because `Answer.answer` is declared `throws Throwable` and Mockito's generated mocks propagate it without the reflective wrapping that would convert it to `UndeclaredThrowableException`. So `thenAnswer(inv -> { throw new IOException(); })` slips a checked exception past a method that does not declare it. It "works", and it is worth being able to explain in an interview, but as a fix it is poor practice: it simulates behaviour the compiler guarantees cannot happen, so no production code path can ever `catch` it in a type-safe way, and a future reader has to work out why the ordinary form was avoided. If you find yourself wanting it, the design question in the previous section is the one to answer instead. ## Related details worth mentioning - `thenThrow(SomeException.class)` asks Mockito to instantiate the exception with its no-arg constructor; a class without one fails, and the resulting exception has no message. Prefer passing an instance when the message matters. - Passing `null` to `thenThrow` is rejected rather than treated as "throw nothing". - The same signature validation applies whichever stubbing style you use, so switching styles is not a route around the rule. ## The short version for an interview "Mockito only lets a mock throw what a real implementation could throw: unchecked exceptions always, checked ones only if declared. When I hit it I treat it as a design question - either the interface should declare the failure, or the production code wraps it in an unchecked exception and my test should use that wrapper."

  • Does the same rule apply to the doThrow form of stubbing, including for void methods?
    Yes. The validation is against the stubbed method's signature, not the stubbing syntax, so doThrow is checked identically - a void method that declares no checked exceptions can only be made to throw unchecked ones. Choosing a different stubbing style is not a way around the rule.

saying these in an interview costs you the question

  • Claiming Mockito never allows checked exceptions at all
  • Reaching straight for a sneaky-throwing Answer instead of asking whether the contract is wrong
  • Thinking doThrow or the BDD style avoids the validation
  • Assuming thenThrow(Class) works for exception classes with no no-arg constructor
  • Believing the rule is arbitrary rather than a consequence of Java's checked-exception contract

context