Mockito creates test doubles for Java interfaces and classes. Its core API
does not depend on JUnit: a program can create a mock, configure behavior,
invoke code, and verify interactions from an ordinary main method. JUnit
integration adds test discovery and automatic mock lifecycle management rather
than basic mocking capability.
Minimal Maven Project Without JUnit #
Put the demonstration under src/test/java because Mockito has test scope:
mockito-hello/
├── pom.xml
└── src/test/java/com/example/MockitoHello.java<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mockito-hello</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<mockito.version>5.23.0</mockito.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>package com.example;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
interface Greeter {
String greet(String name);
}
public final class MockitoHello {
public static void main(String[] args) {
Greeter greeter = mock(Greeter.class);
when(greeter.greet("Ada")).thenReturn("Hello, Ada");
String actual = greeter.greet("Ada");
if (!"Hello, Ada".equals(actual)) {
throw new AssertionError("unexpected result: " + actual);
}
verify(greeter).greet("Ada");
}
}Compile the test class and invoke it with the Maven Exec Plugin:
mvn test-compile \
org.codehaus.mojo:exec-maven-plugin:3.6.1:java \
-Dexec.mainClass=com.example.MockitoHello \
-Dexec.classpathScope=testNo test engine or @Test annotation participates. The process fails through
AssertionError or Mockito’s verification exception.
Mocks and Stubbing #
mock(Greeter.class) returns an object that records method calls. Unstubbed
methods return Mockito defaults such as null, zero, false, or an empty
supported collection.
Ordinary stubbing places the invocation inside when:
when(repository.findById(7L)).thenReturn(user);
when(repository.findById(-1L))
.thenThrow(new IllegalArgumentException("negative ID"));
when(sequence.next()).thenReturn(10, 20, 30);Consecutive return values support stateful protocols, but a fake object can express complex state more clearly than a long chain of stubs.
The do... family handles void methods and cases where evaluating the method
during setup would be unsafe:
doNothing().when(queue).acknowledge("message-1");
doThrow(new IOException("offline")).when(client).close();
doAnswer(invocation -> invocation.getArgument(0))
.when(normalizer).normalize(anyString());Available forms include doReturn, doThrow, doAnswer, doNothing, and
doCallRealMethod.
Verification #
Verification checks collaboration after the exercised code has completed:
verify(repository).save(order);
verify(repository, times(2)).findById(7L);
verify(repository, never()).delete(any());
verifyNoInteractions(auditLog);
verifyNoMoreInteractions(repository);verify(mock) means times(1). Verify only interactions that form part of
the unit’s contract because checks against every implementation call make a
test fail after behavior-preserving refactoring.
InOrder verifies a required sequence across one or more mocks:
InOrder order = inOrder(transaction, publisher);
order.verify(transaction).commit();
order.verify(publisher).publish(event);The example requires publication after commit; unrelated interactions do not need ordered verification.
Argument Matching and Capture #
Mockito compares literal arguments with equals. Matchers relax or describe
that comparison:
when(catalog.find(eq("book"), anyInt())).thenReturn(results);
verify(catalog).find(eq("book"), intThat(limit -> limit > 0));If one argument uses a matcher, every argument in the same invocation must use
a matcher. Wrap literals with eq rather than mixing matcher and literal
forms.
ArgumentCaptor inspects a value created by the unit under test:
ArgumentCaptor<Email> captor = ArgumentCaptor.forClass(Email.class);
verify(sender).send(captor.capture());
assert "ada@example.com".equals(captor.getValue().recipient());Capture during verification. Stubbing with captors hides the input condition and can produce confusing failures when the method is never called.
Answers and Default Behavior #
An Answer computes a result from invocation metadata:
when(cache.compute(anyString())).thenAnswer(invocation -> {
String key = invocation.getArgument(0);
return key.toUpperCase(Locale.ROOT);
});Pass a default answer to mock only when every unstubbed invocation needs
special semantics:
Service service = mock(Service.class, invocation -> {
throw new AssertionError("unstubbed call: " + invocation.getMethod());
});A strict default can expose accidental calls, but it also replaces Mockito’s standard defaults and therefore requires deliberate stubbing.
Spies and Partial Mocks #
A spy invokes real methods unless a method is stubbed:
List<String> names = spy(new ArrayList<>());
doReturn(100).when(names).size();Prefer doReturn(...).when(spy)... because
when(spy.method()).thenReturn(...) calls the real method during setup. A spy
copies the supplied object’s state when created; it is not a continuously
delegating wrapper around the original reference.
Partial mocking couples a test to implementation details. A narrower interface or extracted collaborator provides a stronger boundary when the production design can be changed.
Annotation Lifecycle #
Mockito annotations reduce fixture declarations:
class ReportFixture {
@Mock ReportRepository repository;
@Spy Formatter formatter = new Formatter();
@Captor ArgumentCaptor<Report> reportCaptor;
@InjectMocks ReportService service;
}Without a test-framework integration, initialize and close annotated mocks explicitly:
var fixture = new ReportFixture();
try (AutoCloseable mocks = MockitoAnnotations.openMocks(fixture)) {
// Exercise fixture.service.
}MockitoAnnotations.initMocks is deprecated because it closes the mock
context immediately. Early closure breaks lifecycle-sensitive mock makers and
scoped mocks. @InjectMocks performs constructor, setter, or field injection
as a fixture convenience; it does not replace dependency-injection
configuration.
MockitoSession provides explicit lifecycle and strictness when a custom test
harness needs more control:
MockitoSession session = Mockito.mockitoSession()
.initMocks(fixture)
.strictness(Strictness.STRICT_STUBS)
.startMocking();
try {
// Exercise the fixture.
} finally {
session.finishMocking();
}Strict stubs report unused stubbing and argument mismatches, thus revealing fixture setup that no longer matches exercised behavior.
Final, Static, and Constructor Mocking #
Mockito 5 uses the inline mock maker by default, so mockito-core can mock
final classes, final methods, and enums. Older Mockito releases may require
the former mockito-inline artifact or an explicit mock-maker configuration.
Static and constructor mocks are thread-local scoped resources and must be closed:
try (MockedStatic<ClockSource> clock = mockStatic(ClockSource.class)) {
clock.when(ClockSource::now).thenReturn(fixedInstant);
// Exercise code that calls ClockSource.now().
}
try (MockedConstruction<SocketClient> clients =
mockConstruction(SocketClient.class)) {
// Exercise code that constructs SocketClient.
}Private and native methods cannot be mocked. JVM and mock-maker restrictions also apply to selected JDK types and methods. Static or construction mocking can provide a migration seam for legacy code, but dependency injection avoids global call-site coupling.
Modern JVMs restrict dynamic agent attachment. Builds that use Mockito’s
inline mock maker should follow Mockito’s documented -javaagent
configuration rather than depending on runtime self-attachment.
JUnit 5 Integration #
JUnit Jupiter supplies discovery and lifecycle callbacks. Add the Jupiter integration artifact alongside JUnit:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.13.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>mockito-junit-jupiter transitively includes mockito-core.
MockitoExtension initializes annotation fields and parameters, applies
strict-stub lifecycle handling, and closes mocks after each test:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class GreetingServiceTest {
@Mock
GreetingRepository repository;
@InjectMocks
GreetingService service;
@Test
void returnsTheStoredGreeting() {
when(repository.findFor("Ada")).thenReturn("Welcome, Ada");
assertEquals("Welcome, Ada", service.greet("Ada"));
verify(repository).findFor("Ada");
}
}Method parameters can also receive mocks:
@Test
void retriesAfterFailure(@Mock RemoteClient client) {
// The parameter mock exists only for this invocation.
}Prefer field mocks for collaborators shared by fixture setup and parameter mocks for dependencies local to one test.
Comments
Read or comment on GitHub (sign-in required to comment)