ceritasaham 

â–¸How to Mock in Java With Mockito

How to Mock in Java With Mockito

A practical guide to Mockito in Java: mocks, spies, InjectMocks, static methods, skipped calls, private fields, verification, and common testing patterns.

15 September 2026#java#mockito#testing#junit#unit testing

How to Mock in Java With Mockito

Mockito is a popular Java test framework for replacing collaborators with controlled test doubles. It helps a unit test focus on one class while dependencies such as repositories, HTTP clients, clocks, queues, and external services are replaced by mocks.

Mockito is primarily designed for behavior-based testing:

  • configure what a dependency returns;
  • call the class under test;
  • verify the important interactions;
  • avoid coupling the test to implementation details that do not matter.

This guide uses JUnit 5 examples and modern Mockito APIs.

Dependencies

For Maven, add Mockito Core and the JUnit 5 integration:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>VERSION</version>
    <scope>test</scope>
</dependency>

mockito-junit-jupiter includes the normal Mockito dependency and provides MockitoExtension for JUnit 5. Use a current compatible version instead of copying the placeholder VERSION.

For Gradle:

testImplementation("org.mockito:mockito-junit-jupiter:VERSION")

Most Mockito features work without extra configuration. Mocking static methods, final classes, and final methods uses Mockito's inline mock maker in current Mockito versions. In older Mockito setups, this may require the separate mockito-inline artifact.

Example Classes

The examples use a small service and its collaborators:

public interface UserRepository {
    User findById(long id);
    void save(User user);
}
public interface EmailClient {
    void send(String address, String message);
}
public final class UserService {
    private final UserRepository userRepository;
    private final EmailClient emailClient;

    public UserService(UserRepository userRepository, EmailClient emailClient) {
        this.userRepository = userRepository;
        this.emailClient = emailClient;
    }

    public void sendWelcomeEmail(long userId) {
        User user = userRepository.findById(userId);
        if (user == null) {
            throw new IllegalArgumentException("User not found");
        }
        emailClient.send(user.email(), "Welcome, " + user.name());
    }
}

The service is a good unit-test target because its repository and email client are collaborators. The test should control and verify those collaborators rather than call a real database or mail server.

A Basic Mockito Test

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock
    private UserRepository userRepository;

    @Mock
    private EmailClient emailClient;

    private UserService userService;

    @BeforeEach
    void setUp() {
        userService = new UserService(userRepository, emailClient);
    }

    @Test
    void sendsWelcomeEmail() {
        User user = new User(7L, "Ada", "ada@example.com");
        when(userRepository.findById(7L)).thenReturn(user);

        userService.sendWelcomeEmail(7L);

        verify(emailClient).send("ada@example.com", "Welcome, Ada");
    }
}

@ExtendWith(MockitoExtension.class) initializes the annotated mocks before each test. Without the extension, fields annotated with @Mock, @Spy, @Captor, and @InjectMocks are not initialized automatically.

Creating Mocks Manually

Annotations are convenient, but Mockito also exposes factory methods:

UserRepository userRepository = mock(UserRepository.class);
EmailClient emailClient = mock(EmailClient.class);

UserService userService = new UserService(userRepository, emailClient);

Manual mocks are useful when a test needs different configurations in separate scopes or when a small test does not need the JUnit extension.

What a Mock Does

A mock is a generated test double with no real implementation behavior by default.

UserRepository repository = mock(UserRepository.class);

User user = repository.findById(7L);
assertNull(user); // Object-returning methods return null by default.

repository.save(new User(7L, "Ada", "ada@example.com"));
verify(repository).save(any(User.class));

Typical default values are:

Return typeDefault value
Objectnull
booleanfalse
Numeric primitive0 or 0.0
CollectionUsually an empty collection in modern Mockito defaults
OptionalOptional.empty() in modern Mockito defaults
StreamAn empty stream in modern Mockito defaults
voidDoes nothing

Do not rely heavily on defaults for important behavior. Stubbing the value that matters makes the test intention explicit.

@Mock Versus mock()

These two forms create the same kind of Mockito mock:

@Mock
private UserRepository repository;
UserRepository repository = Mockito.mock(UserRepository.class);

Use @Mock when the Mockito extension already manages the test lifecycle. Use mock() when the mock is local, conditional, or created inside a helper.

Stubbing Return Values

when(...).thenReturn(...)

Use when and thenReturn for ordinary methods that return a value.

when(userRepository.findById(7L)).thenReturn(user);

Multiple calls can return different values:

when(userRepository.findById(7L))
        .thenReturn(firstUser)
        .thenReturn(secondUser)
        .thenThrow(new IllegalStateException("Repository unavailable"));

After the configured values are exhausted, Mockito repeats the last configured answer.

Returning Different Values for Arguments

when(userRepository.findById(1L)).thenReturn(firstUser);
when(userRepository.findById(2L)).thenReturn(secondUser);

thenAnswer(...)

Use thenAnswer when the result depends on the invocation arguments or invocation state.

when(userRepository.findById(anyLong())).thenAnswer(invocation -> {
    long id = invocation.getArgument(0, Long.class);
    return new User(id, "User " + id, "user" + id + "@example.com");
});

A shorter answer can use a lambda:

when(userRepository.findById(anyLong()))
        .thenAnswer(invocation -> new User(
                invocation.getArgument(0),
                "Generated User",
                "generated@example.com"));

thenThrow(...)

when(userRepository.findById(7L))
        .thenThrow(new IllegalStateException("Database unavailable"));

For a checked exception, the method being stubbed must declare that exception.

thenCallRealMethod()

A mock can be configured to call a real method:

UserParser parser = mock(UserParser.class);
when(parser.normalize(" ada ")).thenCallRealMethod();

This is less common than using a spy. Prefer a real object or a spy when most behavior should remain real.

Stubbing Void Methods

when(...) cannot wrap a void method. Use the do...when(...) family instead.

doNothing()

A void method on a mock already does nothing by default, but doNothing can make the intention explicit or override a spy's real behavior.

doNothing().when(emailClient).send(anyString(), anyString());

doThrow()

doThrow(new MailDeliveryException("SMTP unavailable"))
        .when(emailClient)
        .send(anyString(), anyString());

doAnswer()

doAnswer(invocation -> {
    String address = invocation.getArgument(0);
    auditLog.add("Email sent to " + address);
    return null;
}).when(emailClient).send(anyString(), anyString());

doCallRealMethod()

doCallRealMethod().when(emailClient).send(anyString(), anyString());

This is mostly useful for partial mocks and spies. It is usually clearer to construct the real object when real behavior is desired.

How to Skip a Method Call

There are several meanings of "skip a method call" in a test. Choose the one that matches the desired behavior.

Skip a Void Method on a Mock

A void method on a normal mock is already skipped:

EmailClient emailClient = mock(EmailClient.class);
emailClient.send("ada@example.com", "Welcome"); // No real email is sent.

You can state the behavior explicitly:

doNothing().when(emailClient).send(anyString(), anyString());

Skip a Void Method on a Spy

A spy calls real methods by default, so use doNothing to suppress one call:

EmailClient realClient = new SmtpEmailClient();
EmailClient spyClient = spy(realClient);

doNothing().when(spyClient).send(anyString(), anyString());

doNothing is important here. Calling when(spyClient.send(...)) would call the real method while the stubbing expression is evaluated.

Skip a Return-Value Method on a Spy

Use doReturn when a spy should return a value without invoking the real method:

UserRepository spyRepository = spy(new FileUserRepository());
doReturn(user).when(spyRepository).findById(7L);

Skip a Particular Internal Call

Mockito does not provide a general "skip this private call" feature for normal objects. If an internal operation should be replaceable, extract it behind a collaborator and inject that collaborator:

public class InvoiceService {
    private final TaxCalculator taxCalculator;

    public InvoiceService(TaxCalculator taxCalculator) {
        this.taxCalculator = taxCalculator;
    }

    public Invoice create(InvoiceDraft draft) {
        Money tax = taxCalculator.calculate(draft);
        return new Invoice(draft.total(), tax);
    }
}

Now the test can skip the real tax calculation by mocking TaxCalculator:

when(taxCalculator.calculate(draft)).thenReturn(Money.zero());

This design is normally more maintainable than mocking a private method.

@Spy Versus @InjectMocks

@Spy and @InjectMocks are not alternatives. They answer different questions.

AnnotationPurposeCreates a mock?Calls real methods by default?
@MockCreates a fully mocked collaborator.YesNo
@SpyWraps a real object for partial mocking.PartiallyYes
@InjectMocksCreates the class under test and injects mocks/spies into it.No, unless the class itself is also annotated separatelyYes

@Spy

A spy wraps a real instance. Unstubbed methods call the real implementation.

@Spy
private ArrayList<String> names = new ArrayList<>();

@Test
void usesRealListBehaviorButStubsOneMethod() {
    names.add("Ada");
    doReturn(100).when(names).size();

    assertEquals(100, names.size());
    assertEquals("Ada", names.get(0));
}

A spy is useful when most of the real behavior is desirable and only one or two interactions need control. It can also signal that the class has too many responsibilities if a test needs to stub many methods.

@InjectMocks

@InjectMocks creates the object under test and tries to inject available mocks and spies. Mockito generally attempts constructor injection first, then setter injection, then field injection.

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock
    private UserRepository userRepository;

    @Mock
    private EmailClient emailClient;

    @InjectMocks
    private UserService userService;

    @Test
    void sendsWelcomeEmail() {
        User user = new User(7L, "Ada", "ada@example.com");
        when(userRepository.findById(7L)).thenReturn(user);

        userService.sendWelcomeEmail(7L);

        verify(emailClient).send("ada@example.com", "Welcome, Ada");
    }
}

@InjectMocks is not a mock of UserService; it is a real UserService instance. Its methods execute normally.

Combining @Spy and @InjectMocks

A real collaborator can be spied and injected into the class under test:

@Spy
private PricingService pricingService = new PricingService();

@InjectMocks
private CheckoutService checkoutService;

Use this sparingly. Most unit tests are clearer when all external collaborators are mocks and the class under test is constructed explicitly.

Safe Stubbing of Spies

This can call the real method before Mockito applies the stub:

// Risky for a spy: the real method is evaluated first.
when(spyRepository.findById(7L)).thenReturn(user);

Prefer doReturn for spies:

doReturn(user).when(spyRepository).findById(7L);

Use the corresponding doThrow, doAnswer, doNothing, and doCallRealMethod forms for spy methods, especially when the real method has side effects, requires initialized state, or throws an exception.

Mocking Static Methods

Static mocking is scoped and should be closed after the test. Use MockedStatic in a try-with-resources block.

Suppose the production code uses:

public final class IdGenerator {
    public static String generate() {
        return UUID.randomUUID().toString();
    }
}

The test can mock the static method:

@Test
void usesDeterministicGeneratedId() {
    try (MockedStatic<IdGenerator> mocked = mockStatic(IdGenerator.class)) {
        mocked.when(IdGenerator::generate).thenReturn("fixed-id");

        String id = service.createId();

        assertEquals("fixed-id", id);
        mocked.verify(IdGenerator::generate);
    }
}

A static mock affects the current thread while the MockedStatic scope is open. Closing the scope restores the original static behavior.

Static Method With Arguments

try (MockedStatic<Files> mocked = mockStatic(Files.class)) {
    mocked.when(() -> Files.exists(path)).thenReturn(true);
    mocked.when(() -> Files.readString(path)).thenReturn("content");

    // Call the class under test here.
}

Verify a Static Call

try (MockedStatic<ClockProvider> mocked = mockStatic(ClockProvider.class)) {
    mocked.when(ClockProvider::systemClock).thenReturn(fixedClock);

    service.run();

    mocked.verify(ClockProvider::systemClock);
}

Static Mock With a Default Answer

try (MockedStatic<IdGenerator> mocked = mockStatic(
        IdGenerator.class,
        Answers.CALLS_REAL_METHODS)) {
    mocked.when(IdGenerator::generate).thenReturn("fixed-id");
}

This leaves unstubbed static methods calling their real implementations. Use it only when that mixed behavior is intentional.

Why Static Mocking Should Be Limited

Static mocking is useful for legacy code, time providers, UUID generation, and difficult-to-replace APIs. However, frequent static mocking can hide a design problem. A wrapper such as IdGenerator, Clock, or FileSystem can be injected as a normal collaborator and mocked without static scope management.

Mocking Final Classes and Methods

Modern Mockito can mock final classes and final methods when its inline mock maker is available:

final class PaymentGateway {
    final PaymentResult charge(Money amount) {
        return new PaymentResult("approved");
    }
}

PaymentGateway gateway = mock(PaymentGateway.class);
when(gateway.charge(any())).thenReturn(new PaymentResult("declined"));

If this fails in an older project, check the Mockito version and mock-maker configuration. Do not remove final from production code only to make a unit test easier.

Working With Private Variables

Prefer Constructor Injection

The cleanest way to control a private dependency is constructor injection:

class ReportService {
    private final ReportRepository repository;

    ReportService(ReportRepository repository) {
        this.repository = repository;
    }
}

The test can pass a mock directly:

ReportRepository repository = mock(ReportRepository.class);
ReportService service = new ReportService(repository);

The field can remain private and final; no special Mockito access is needed.

@InjectMocks and Private Fields

If constructor injection is unavailable, @InjectMocks may inject matching mocks into fields or setters:

@Mock
private ReportRepository repository;

@InjectMocks
private ReportService service;

Injection is based on available types and, in some cases, names. It is not a replacement for a clear constructor. If Mockito cannot resolve a dependency, construct the class explicitly or refactor the production class.

ReflectionTestUtils for Legacy Code

Spring projects sometimes use ReflectionTestUtils to set a private field in legacy code:

ReflectionTestUtils.setField(service, "repository", repository);

This is a test-only escape hatch, not a preferred design. It couples the test to the private field name and can break during harmless refactoring.

Reading a Private Variable

A unit test usually should verify observable behavior instead of reading private state. If private state must be inspected in legacy code, use a test utility such as Spring's ReflectionTestUtils or Java reflection as a last resort.

Object value = ReflectionTestUtils.getField(service, "repository");
assertSame(repository, value);

Testing a private field directly usually produces a brittle test. Prefer a public result, a collaborator interaction, or a domain-level query.

Private Methods

Mockito does not support ordinary mocking of private methods. The recommended options are:

  1. test the private method through the public method that uses it;
  2. extract the behavior into a collaborator and mock that collaborator;
  3. refactor a complex private method into a separate class with a public contract.

Legacy tools that mock private methods add complexity and can make tests tightly coupled to implementation details. Use them only when a legacy migration requires it and the project explicitly accepts that tradeoff.

Argument Matchers

Mockito provides matchers for flexible stubbing and verification.

when(userRepository.findById(anyLong())).thenReturn(user);
verify(emailClient).send(eq("ada@example.com"), contains("Welcome"));

Common matchers include:

MatcherMatches
any()Any object, including many nullable values depending on type and Mockito version.
anyString()Any non-null string.
anyLong()Any primitive long or boxed Long compatible with the invocation.
eq(value)A value equal to the supplied value.
same(value)The exact same object reference.
contains(text)A string containing the supplied text.
startsWith(text)A string beginning with the supplied text.
argThat(predicate)A custom condition.
isNull()A null argument.
isA(Type.class)An object of the specified type.

Do Not Mix Raw Values and Matchers

When one argument uses a matcher, all arguments in that method call must use matchers:

// Invalid matcher usage:
// verify(emailClient).send(anyString(), "Welcome");

verify(emailClient).send(anyString(), eq("Welcome"));

Use eq for literal values in a call that already contains another matcher.

Custom Argument Matching

verify(userRepository).save(argThat(saved ->
        saved.name().equals("Ada") && saved.email().endsWith("@example.com")));

If the predicate is complicated, use an ArgumentCaptor instead so the failure message is easier to understand.

Capturing Arguments

ArgumentCaptor records an argument passed to a mock so the test can inspect it afterward.

@Captor
private ArgumentCaptor<User> userCaptor;

@Test
void savesNormalizedUser() {
    service.register(" Ada ", "ADA@EXAMPLE.COM");

    verify(userRepository).save(userCaptor.capture());
    User saved = userCaptor.getValue();

    assertEquals("Ada", saved.name());
    assertEquals("ada@example.com", saved.email());
}

The annotation requires MockitoExtension, or the captor can be created manually:

ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);

Prefer a captor when the argument's structure is part of the behavior under test. Do not capture every argument automatically; direct verify calls are simpler for exact values.

Verification

Verify a Call

verify(emailClient).send("ada@example.com", "Welcome, Ada");

Verify Call Count

verify(emailClient, times(1)).send(anyString(), anyString());
verify(emailClient, never()).send("other@example.com", anyString());
verify(emailClient, atLeastOnce()).send(anyString(), anyString());
verify(emailClient, atMost(2)).send(anyString(), anyString());

Use exact counts only when the number of calls is part of the contract. Otherwise, never, atLeastOnce, or a plain verify can make a test less fragile.

Verify No More Interactions

verify(emailClient).send(anyString(), anyString());
verifyNoMoreInteractions(emailClient);

verifyNoMoreInteractions is strict: it fails when any unverified interaction occurred. Use it only when extra calls would indicate a real defect.

Verify No Interactions

verifyNoInteractions(emailClient);

This is useful for a branch where the service must not send an email, publish an event, or call an external service.

Verify Order

InOrder order = inOrder(userRepository, emailClient);
order.verify(userRepository).findById(7L);
order.verify(emailClient).send(anyString(), anyString());

Order verification should be used only when call order affects behavior. Tests should not fail merely because independent calls were reordered.

Verify With a Timeout

For asynchronous code:

verify(eventPublisher, timeout(1_000)).publish(any(Event.class));

Prefer deterministic synchronization such as a future, latch, or test scheduler when possible. Timeout-based tests can be slower and more prone to timing issues.

Resetting and Clearing Mocks

clearInvocations

Removes recorded interactions but keeps stubbing:

clearInvocations(repository);

reset

Removes both stubbing and recorded interactions:

reset(repository);

Avoid resetting mocks in the middle of a test. Separate tests usually provide clearer setup and assertions. Resetting is more appropriate for carefully managed shared fixtures or framework integrations.

Strict Stubbing

The JUnit 5 Mockito extension uses strict stubbing by default in many configurations. It can report:

  • a stub that was never used;
  • an argument that differs from the configured stub;
  • unnecessary or contradictory test setup.

For example:

when(repository.findById(99L)).thenReturn(user);
service.sendWelcomeEmail(7L);

The unused 99L stub probably indicates a test mistake. Remove unused stubs instead of disabling strictness globally.

If a specific stub is intentionally optional, use lenient sparingly:

lenient().when(repository.findById(7L)).thenReturn(user);

Deep Stubs

Mockito can create chained mocks with RETURNS_DEEP_STUBS:

OrderService service = mock(OrderService.class, RETURNS_DEEP_STUBS);
when(service.currentOrder().customer().email()).thenReturn("ada@example.com");

Deep stubs are usually a design warning. They make a test depend on a chain of internal calls and can hide excessive coupling. Prefer a collaborator method that expresses the needed operation directly.

Spies and Real Partial Behavior

A spy can be created from an existing object:

InvoiceCalculator calculator = new InvoiceCalculator();
InvoiceCalculator spyCalculator = spy(calculator);

Or with an annotation:

@Spy
private InvoiceCalculator calculator = new InvoiceCalculator();

Unstubbed methods use the real object:

spyCalculator.calculateSubtotal(items); // Real method runs.

Stub only the behavior that is expensive, nondeterministic, or outside this test's responsibility:

doReturn(fixedTax).when(spyCalculator).calculateTax(any());

Spies should not be used as a default replacement for mocks. A spy can execute database, network, file, or time-dependent code unexpectedly if one method was not stubbed.

Mocking Constructors

Modern Mockito can mock construction with MockedConstruction. The mock is scoped and should be closed.

try (MockedConstruction<ExpensiveClient> mocked = mockConstruction(
        ExpensiveClient.class,
        (mock, context) -> when(mock.fetch()).thenReturn("test-data"))) {

    Service service = new Service();
    String result = service.run();

    assertEquals("test-data", result);
    assertEquals(1, mocked.constructed().size());
}

Constructor mocking is useful for legacy code that directly creates dependencies. For new code, dependency injection is generally simpler and more explicit.

Mocking Static Methods Versus Injecting Wrappers

Static mocking:

try (MockedStatic<ClockProvider> mocked = mockStatic(ClockProvider.class)) {
    mocked.when(ClockProvider::systemClock).thenReturn(fixedClock);
    service.run();
}

Injectable wrapper:

ClockProvider clockProvider = mock(ClockProvider.class);
when(clockProvider.systemClock()).thenReturn(fixedClock);
Service service = new Service(clockProvider);

The wrapper approach usually gives simpler test lifecycle management, clearer dependencies, and less risk of leaking static behavior between tests. Static mocking remains valuable when changing the production design is not practical.

Common Mockito Exceptions

NotAMockException

This occurs when a Mockito operation receives a real object:

UserService service = new UserService(repository, emailClient);
// verify(service); // Invalid: service is not a mock.

Verify the collaborator mock instead:

verify(emailClient).send(anyString(), anyString());

MissingMethodInvocationException

This often happens when when is used with a void method, a final method that cannot currently be mocked, or a real object:

// when(emailClient.send(...)).thenThrow(...); // Invalid for void methods.
doThrow(exception).when(emailClient).send(anyString(), anyString());

UnfinishedStubbingException

Check for incomplete stubbing, nested calls that invoke real behavior unexpectedly, or a missing thenReturn, thenThrow, or thenAnswer.

PotentialStubbingProblem

The invocation arguments differ from the stubbing arguments under strict stubbing. Correct the arguments or use a matcher intentionally:

when(repository.findById(eq(7L))).thenReturn(user);

InvalidUseOfMatchersException

This usually means raw values and matchers were mixed in one invocation, or a matcher was used outside a Mockito stubbing or verification call.

MockitoException for Static Mocking

Check that:

  • the Mockito version supports inline static mocking;
  • the inline mock maker is enabled for the project version;
  • the static mock is not already registered on the same thread;
  • the MockedStatic scope is closed;
  • the test is not running with a conflicting Java agent.

Best Practices

  1. Mock collaborators, not the class under test.
  2. Prefer constructor injection over private-field reflection.
  3. Use @InjectMocks for convenience, but construct the class explicitly when setup clarity matters.
  4. Use @Spy only when real behavior is intentionally retained.
  5. Use doReturn and doNothing when stubbing spies.
  6. Keep static mocks inside a try-with-resources scope.
  7. Verify meaningful behavior, not every implementation detail.
  8. Use argument matchers consistently within one invocation.
  9. Prefer a fake or in-memory implementation when it communicates behavior better than a large mock setup.
  10. Remove unused stubbing and keep tests independent.
  11. Avoid mocking private methods; extract collaborators instead.
  12. Keep asynchronous verification deterministic whenever possible.

Quick Reference

GoalMockito form
Create a mockmock(Type.class) or @Mock
Create a partial mockspy(realObject) or @Spy
Create the class under test@InjectMocks or an explicit constructor call
Stub a return valuewhen(mock.call()).thenReturn(value)
Stub a dynamic resultwhen(mock.call()).thenAnswer(answer)
Throw from a return-value methodwhen(mock.call()).thenThrow(exception)
Skip a void methoddoNothing().when(mock).call()
Throw from a void methoddoThrow(exception).when(mock).call()
Stub a spy safelydoReturn(value).when(spy).call()
Mock a static methodtry (MockedStatic<Type> mocked = mockStatic(Type.class)) { ... }
Verify a callverify(mock).call()
Verify no callverify(mock, never()).call()
Capture an argumentArgumentCaptor
Clear recorded callsclearInvocations(mock)
Remove stubbing and callsreset(mock)

Mockito works best when it supports a clear design rather than compensating for a difficult one. Start with dependency injection and focused collaborators, then use the smallest Mockito feature that expresses the behavior the test needs to control or verify.

▸mode [MARKET REGULER]build #ba129dblast update: Sep 22, 2026, 07:43:27 PM UTC▚▚ DYOR! Disclaimer ON! ▚▚