What is Mockito's constraint on stubbing checked exceptions, and why does it exist?
answer
- checked = Exception but not RuntimeException
- checked stubbable only if declared (or supertype) in throws
- unchecked = always allowed
- error message: 'Checked exception is invalid for this method!'
- rule mirrors the compiler → test fidelity
basics
~10 sYou can only stub a checked exception that the method actually declares with throws. Unchecked exceptions (RuntimeException/Error) are always allowed. Otherwise Mockito fails with "Checked exception is invalid for this method!".
solid answer
~50 sJava forces callers to handle or declare checked exceptions, so a method can only propagate a checked exception it lists in its throws clause. Mockito mirrors that compiler rule: thenThrow/doThrow let you stub a checked exception only if the stubbed method declares it (or a supertype of it); otherwise you get MockitoException: "Checked exception is invalid for this method!". This prevents tests from simulating something that could never happen in real code, which would make the test meaningless. Unchecked exceptions — anything extending RuntimeException or Error — are not subject to this rule and can be thrown from any method. So if you need to simulate a checked failure the method doesn't declare, the method's signature is the real constraint; you'd typically wrap it in an unchecked exception or stub a checked type the signature permits.
go deeper
Aware that unchecked exceptions always work but checked ones sometimes fail when stubbed.
States the rule: a checked exception must be declared by the method, and recognizes the resulting MockitoException message.
Explains the rationale (mirroring the compiler for test fidelity), the supertype allowance, and how to work within the constraint.
Reads the constraint as design feedback — wrapping checked exceptions at boundaries, designing signatures intentionally, and avoiding tests that simulate impossible states.
## Checked vs. unchecked exceptions in Java Java splits `Throwable` into two families relevant here: - **Checked exceptions:** subclasses of `Exception` that are **not** subclasses of `RuntimeException` (e.g. `IOException`, `SQLException`, `TimeoutException`). The compiler enforces a contract: any method that can throw one must either catch it or **declare** it in a `throws` clause, and callers must handle/declare it too. - **Unchecked exceptions:** `RuntimeException` and its subclasses (e.g. `IllegalArgumentException`, `NullPointerException`), plus `Error` and its subclasses. These need **no** declaration and can propagate from anywhere. ## The Mockito rule When you stub with `thenThrow(...)` or `doThrow(...)`, Mockito checks: *could this method legally throw this exception at runtime?* - If the exception is **unchecked**, the answer is always yes — allowed. - If the exception is **checked**, Mockito allows it **only if** the stubbed method's signature declares that exception **or a superclass of it** in its `throws` clause. Otherwise it throws: ``` org.mockito.exceptions.base.MockitoException: Checked exception is invalid for this method! Invalid: java.io.IOException ``` ## Why the rule exists The purpose is fidelity. In real production code, a method that does not declare `throws IOException` can **never** propagate an `IOException` to its caller — the compiler forbids it. If Mockito let your test make such a method throw `IOException`, the test would be exercising a scenario that is impossible in reality. That is worse than useless: it can give false confidence or force you to write `catch` blocks for paths that can never execute. By mirroring the compiler's contract, Mockito keeps stubs consistent with what the type system permits. ```java interface Repo { User find(long id); // declares nothing void save(User u) throws IOException; // declares IOException } // OK: unchecked on any method when(repo.find(1L)).thenThrow(new IllegalStateException()); // OK: checked that save() declares doThrow(new IOException()).when(repo).save(user); // FAILS: find() doesn't declare IOException (a checked type) when(repo.find(1L)).thenThrow(new IOException()); ``` ## Working within the constraint If you genuinely need to simulate a failure on a method whose signature does not permit the checked type, the constraint is telling you something about your design. Options: 1. Throw an **unchecked** exception that the production code would actually see (often the realistic case — many frameworks wrap checked exceptions, e.g. Spring's `DataAccessException`). 2. Stub a checked exception the signature **does** declare. 3. If the method really should be able to throw that checked type, fix its signature. ## Edge note: supertypes Declaring `throws Exception` permits stubbing *any* checked exception, since all are subtypes of `Exception`. Declaring `throws IOException` permits `IOException` and its subclasses (e.g. `FileNotFoundException`). ## Summary - Unchecked exceptions: stubbable anywhere. - Checked exceptions: stubbable only if declared (or a supertype is) by the method. - The rule mirrors the Java compiler so stubs stay true to what can really happen.
- How can a method declaring 'throws Exception' affect stubbing?It permits stubbing any checked exception, because every checked exception is a subtype of Exception.
- What do you do if you need to simulate a checked failure the method doesn't declare?Stub an unchecked exception the production path would actually surface, stub a checked type the signature allows, or fix the signature if it truly should throw it.
saying these in an interview costs you the question
- Claiming any exception can be stubbed regardless of the signature
- Confusing checked/unchecked (e.g. calling NullPointerException checked)
- Thinking the rule is arbitrary rather than mirroring Java's throws contract