Mocking An Entire Class
ID |
java.mocking_entire_class |
Severity |
high |
Remediation Complexity |
hard |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Reliability |
Language |
Java |
Tags |
mockito, reliability, testing |
Description
| Disabled by default. Stubbing every method of a type says nothing about whether the subject under test is exercised: in a review of real projects the fully stubbed type was a collaborator and the subject a different class in every case. Reporting that correctly would require knowing that the mocked type is the type under test, which this rule cannot determine. |
Mockito lets a test stub any subset of a class’s methods, but when a test ends up stubbing every accessible method of the mocked class, the mock has become a full stand-in for the real type. At that point the test is only exercising the stub program it just wrote — it can never fail for the reason it claims to test, because nothing in the class under test actually runs.
This rule reports a mock(X.class) whose mock object has every non-private method of X
stubbed, in the same method, through when(…)/given(…). A class with a single non-private
method is not reported: with only one member there is no "rest of the class" left unstubbed.
Rationale
interface PaymentGateway {
boolean charge(String cardToken, int amountCents);
String refund(String transactionId);
}
@Test
void refundsAfterAFailedCharge() {
PaymentGateway gateway = mock(PaymentGateway.class); // Noncompliant
when(gateway.charge("tok", 500)).thenReturn(false);
when(gateway.refund("txn")).thenReturn("REF-1");
// everything below runs entirely against the stub program above
}
Both methods of PaymentGateway are stubbed, so gateway behaves exactly as this test tells it
to and nothing else — there is no real PaymentGateway implementation left to fail.
Remediation
Either use a real (or purpose-built test) implementation of the class instead of mocking it entirely, or split the stubbing across separate, focused tests so each one only mocks the single method it actually needs.
@Test
void chargeIsAttempted() {
PaymentGateway gateway = mock(PaymentGateway.class);
when(gateway.charge("tok", 500)).thenReturn(false);
// ...
}
@Test
void refundIsIssued() {
PaymentGateway gateway = mock(PaymentGateway.class);
when(gateway.refund("txn")).thenReturn("REF-1");
// ...
}
Limitations
The mocked class is very often declared in another file, so its member list is read from the
project-wide type model rather than the current compilation unit. A mock whose type has no entry
in that model — a third-party type, or a scan where the model was not built — is never reported,
since an unresolved member list would otherwise read as "everything is stubbed" for a class this
rule cannot actually see. Only the class’s own declared methods are considered, not methods
reached only through a supertype. Stubbing through Mockito’s doReturn(…).when(mock).method()
form is not counted toward "stubbed", only when(…)/given(…).