Mockito without and with JUnit 5

Last update: 2026-08-17 10:18:23 +0800 +0800
Available translations: 中文(简体) / 中文(繁體)

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:

text
mockito-hello/
├── pom.xml
└── src/test/java/com/example/MockitoHello.java
xml
<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>
java
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:

sh
mvn test-compile \
  org.codehaus.mojo:exec-maven-plugin:3.6.1:java \
  -Dexec.mainClass=com.example.MockitoHello \
  -Dexec.classpathScope=test

No 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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

java
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:

xml
<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:

java
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:

java
@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.

References #

Comments

Read or comment on GitHub (sign-in required to comment)