CodeGym /Courses /Module 5. Spring /Lecture 133: Mockito for creating mocks and stubs

Lecture 133: Mockito for creating mocks and stubs

Module 5. Spring
Level 9 , Lesson 2
Available

When you're testing an application, you often run into situations where your code depends on other components like services, repositories, external APIs, or even third-party libraries. Instead of running the real code of those dependencies in tests (and wasting a bunch of resources), we can use mocks — "fakes" that emulate the behavior of real objects. And that's where Mockito comes in.

Picture this: you're writing a test for a service that calls a database. A real call to the database could take too long, and it might even introduce SQL-dependency issues... that happens. Instead, with Mockito, we just replace the DB call with a special object that returns a pre-set result. Handy, right?


Mockito core concepts

Before jumping into practical stuff, let's unpack the terms mocks, stubs, and spies.

  • Mock — an object that imitates the behavior of a real object. With it you can define predefined responses to specific calls.
  • Stub — similar to a mock but less flexible. It's basically a "plain" object with preprogrammed behavior used for testing.
  • Spy — a real object, but with the ability to observe how it's used. Sometimes you need the real functionality but want to override parts of its behavior.

Getting started with Mockito

To use Mockito, add its dependency to your project.

Adding Mockito via Maven


<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.0.0</version> <!-- Replace with the current version -->
    <scope>test</scope>
</dependency>

If you use Gradle:


testImplementation 'org.mockito:mockito-core:5.0.0' // Replace with the current version

Also make sure to include JUnit if it's not already in the project.


Main features of Mockito

1. Creating mocks

You can create a mock using the mock() method:


import static org.mockito.Mockito.*;

SomeService someServiceMock = mock(SomeService.class);

However, if you're using JUnit 5, we recommend using the @Mock annotation for better readability. You'll need @ExtendWith(MockitoExtension.class) for that.

Example:


import static org.mockito.Mockito.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class SomeServiceTest {

    @Mock
    private SomeService someServiceMock;

    // Your tests go here
}

2. Configuring mock behavior

After creating a mock, the interesting part is defining its behavior. For example, we want a method to return a specific value:


when(someServiceMock.someMethod()).thenReturn("Mocked Value");

Usage example:


@Test
void testMockBehavior() {
    when(someServiceMock.greet("John")).thenReturn("Hello, mocked John!");

    String result = someServiceMock.greet("John");

    assertEquals("Hello, mocked John!", result);
}
The call when().thenReturn() literally lets you "teach" the mock what it should do.

3. Verifying calls

Mockito lets you not only define behavior but also verify whether a specific method was called — and how many times.


@Test
void testMethodInvocation() {
    someServiceMock.greet("John");

    verify(someServiceMock).greet("John"); // Check: was greet called with "John"
    verify(someServiceMock, times(1)).greet("John"); // Check: called exactly once
}
If the method wasn't called, the test will fail with an error like: "Wanted but not invoked".

Mockito annotations

To make code simpler, Mockito provides several handy annotations:

  1. @Mock: Creates a mock for the specified object.
    
    @Mock
    private SomeService someServiceMock;
    
  2. @InjectMocks: Automatically injects mocks into the dependencies of the object under test.
    
    @InjectMocks
    private SomeController someController;
    

    Example:

    
    @Test
    void testController() {
        when(someServiceMock.greet("John")).thenReturn("Hello, John!");
    
        String response = someController.greet("John");
    
        assertEquals("Hello, John!", response);
    }
    
  3. @Spy: Creates a real object but allows overriding some of its methods.
    
    @Spy
    private SomeService realService;
    

Practice: testing a service

We have a service GreetingService that depends on TimeProvider to form a greeting based on the current time:


public class GreetingService {
    private final TimeProvider timeProvider;

    public GreetingService(TimeProvider timeProvider) {
        this.timeProvider = timeProvider;
    }

    public String getGreeting(String name) {
        int currentHour = timeProvider.getCurrentHour();

        if (currentHour < 12) {
            return "Good morning, " + name + "!";
        } else {
            return "Good afternoon, " + name + "!";
        }
    }
}

In tests we mock TimeProvider to verify the service behavior:


import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class GreetingServiceTest {

    private final TimeProvider timeProviderMock = mock(TimeProvider.class);
    private final GreetingService greetingService = new GreetingService(timeProviderMock);

    @Test
    void testMorningGreeting() {
        when(timeProviderMock.getCurrentHour()).thenReturn(9); // Morning

        String greeting = greetingService.getGreeting("John");

        assertEquals("Good morning, John!", greeting);
    }

    @Test
    void testAfternoonGreeting() {
        when(timeProviderMock.getCurrentHour()).thenReturn(15); // Afternoon

        String greeting = greetingService.getGreeting("John");

        assertEquals("Good afternoon, John!", greeting);
    }
}
Here TimeProvider can be considered an external dependency that we've mocked.

Common mistakes when using Mockito

Some mistakes can unexpectedly break your tests:

  1. NullPointerException: You forgot to initialize mocks. Fix: use @Mock and @ExtendWith(MockitoExtension.class).
  2. Mismatch between behavior and invocation: If the method is called with different parameters than those you specified in when(). Always verify your argument correctness.
  3. Excessive mock behavior: "Teach" mocks only what you need for the specific test, otherwise your test will become hard to maintain.

Mockito is a powerful tool that makes testing complex systems easier and more convenient. Now you know how to create mocks, configure their behavior, and verify interactions. In the next lectures you'll see how to apply all this for testing controllers and other components of Spring applications.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION