skip to content

What is Mockito's constraint on stubbing checked exceptions, and why does it exist?

level: seniorimportance: should knowfreq 50%

answer

  1. checked = Exception but not RuntimeException
  2. checked stubbable only if declared (or supertype) in throws
  3. unchecked = always allowed
  4. error message: 'Checked exception is invalid for this method!'
  5. rule mirrors the compiler → test fidelity

basics

~10 s

You 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 s

Java 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

for a junior

Aware that unchecked exceptions always work but checked ones sometimes fail when stubbed.

for a middle

States the rule: a checked exception must be declared by the method, and recognizes the resulting MockitoException message.

for a senior

Explains the rationale (mirroring the compiler for test fidelity), the supertype allowance, and how to work within the constraint.

for a principal

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

context