Spring Boot + Mockito example unit testing

Introduction

Are you still afraid of writing tests? Do you think it’s complicated and time-consuming? Relax! It’s much easier than it seems. With Mockito, we can easily create isolated tests that help us verify service operations without touching the real database. In this article, we’ll explore how to properly use @Mock and @InjectMocks to make your code reliable and your tests fast and efficient. Let’s go!


What is Mockito?

Mockito is a popular framework for creating mock objects in Java applications. Mocks are used to simulate dependencies, allowing you to test individual components without real interactions with the database, external APIs, or other modules.

Key Mockito Annotations

  • @Mock – Creates a mock object for a specified class or interface. It allows replacing the real behavior of dependencies.
  • @InjectMocks – Creates an instance of the class under test and automatically injects dependencies annotated with @Mock.
  • @ExtendWith(MockitoExtension.class) – Integrates Mockito with JUnit 5, enabling the use of @Mock and @InjectMocks without additional setup.

Example usage:

@ExtendWith(MockitoExtension.class)
public class DeviceServiceTest {
    @Mock
    private DeviceRepository deviceRepository;

    @InjectMocks
    private DeviceService deviceService;
}

 


Project Setup

To get started, create a Spring Boot project using Spring Initializr with the following dependencies:

  • Spring Web – to create REST APIs.
  • Spring Data JPA – for database interaction.
  • H2 Database – an in-memory database for testing.
  • Lombok – to reduce boilerplate code.
  • Spring Boot Starter Test – includes Mockito, JUnit, and other testing libraries.

Complete pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>tech.devblueprint</groupId>
    <artifactId>mock-testing-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mock-testing-example</name>
    <description>Demonstration of Mockito Mock and InjectMocks usage in Spring Boot</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

Now, let’s create the business logic, including Entity, Repository, Service, and Controller.

1. Creating the Device Entity

package tech.devblueprint.mock_testing_example.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "devices")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Device {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String name;

    @Column(nullable = false)
    private String type;

    @Column(nullable = false)
    private boolean active;
}

 

 

2. Creating the Repository

package tech.devblueprint.mock_testing_example.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import tech.devblueprint.mock_testing_example.entity.Device;

public interface DeviceRepository extends JpaRepository<Device, Long> {
}

 

3. Creating the Service Layer

package tech.devblueprint.mock_testing_example.service;


import org.springframework.stereotype.Service;
import tech.devblueprint.mock_testing_example.entity.Device;
import tech.devblueprint.mock_testing_example.repository.DeviceRepository;

import java.util.List;
import java.util.Optional;

@Service
public class DeviceService {

    private final DeviceRepository deviceRepository;

    public DeviceService(DeviceRepository deviceRepository) {
        this.deviceRepository = deviceRepository;
    }

    public Device createDevice(Device device) {
        return deviceRepository.save(device);
    }

    public Device getDeviceById(Long id) {
        Optional<Device> device = deviceRepository.findById(id);
        return device.orElse(null);
    }

    public List<Device> getAllDevices() {
        return deviceRepository.findAll();
    }
}

 

4. Creating the REST Controller

package tech.devblueprint.mock_testing_example.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import tech.devblueprint.mock_testing_example.entity.Device;
import tech.devblueprint.mock_testing_example.service.DeviceService;

import java.util.List;

@RestController
@RequestMapping("/api/devices")
public class DeviceController {

    private final DeviceService deviceService;

    public DeviceController(DeviceService deviceService) {
        this.deviceService = deviceService;
    }

    @PostMapping
    public ResponseEntity<Device> createDevice(@RequestBody Device device) {
        return ResponseEntity.ok(deviceService.createDevice(device));
    }

    @GetMapping("/{id}")
    public ResponseEntity<Device> getDeviceById(@PathVariable Long id) {
        return ResponseEntity.ok(deviceService.getDeviceById(id));
    }

    @GetMapping
    public ResponseEntity<List<Device>> getAllDevices() {
        return ResponseEntity.ok(deviceService.getAllDevices());
    }
}

 

Thus, we have a working REST API for managing devices.


Writing Tests

Now, let’s test our code using Mockito and JUnit 5.

1. Testing the Controller

package tech.devblueprint.mock_testing_example.controller;

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;
import org.springframework.http.ResponseEntity;
import tech.devblueprint.mock_testing_example.entity.Device;
import tech.devblueprint.mock_testing_example.service.DeviceService;

import java.util.List;

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

@ExtendWith(MockitoExtension.class)
class DeviceControllerTest {

    @Mock
    private DeviceService deviceService;

    @InjectMocks
    private DeviceController deviceController;

    @Test
    void testFindAllDevices() {
        List<Device> devices = List.of(
                new Device(1L, "Laptop", "Electronics", true),
                new Device(2L, "Smartphone", "Electronics", true)
        );

        when(deviceService.getAllDevices()).thenReturn(devices);
        ResponseEntity<List<Device>> response = deviceController.getAllDevices();
        assertEquals(2, response.getBody().size());
    }

    @Test
    void testCreateDevice() {
        Device device = new Device(1L, "Laptop", "Electronics", true);

        when(deviceService.createDevice(device)).thenReturn(device);
        ResponseEntity<Device> response = deviceController.createDevice(device);
        assertEquals("Laptop", response.getBody().getName());
    }

    @Test
    void testFindDeviceById() {
        Device device = new Device(1L, "Laptop", "Electronics", true);
        when(deviceService.getDeviceById(1L)).thenReturn(device);
        ResponseEntity<Device> response = deviceController.getDeviceById(1L);
        assertEquals("Laptop", response.getBody().getName());
    }
}

 

2. Testing the Service Layer

package tech.devblueprint.mock_testing_example.service;

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;
import tech.devblueprint.mock_testing_example.entity.Device;
import tech.devblueprint.mock_testing_example.repository.DeviceRepository;

import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class DeviceServiceTest {

    @InjectMocks
    private DeviceService deviceService;

    @Mock
    private DeviceRepository deviceRepository;

    @Test
    void testFindAllDevices() {
        List<Device> devices = List.of(
                new Device(1L, "Laptop", "Electronics", true),
                new Device(2L, "Smartphone", "Electronics", true)
        );

        when(deviceRepository.findAll()).thenReturn(devices);

        List<Device> result = deviceService.getAllDevices();

        assertEquals(2, result.size());
    }

    @Test
    void testCreateDevice() {
        Device device = new Device(1L, "Laptop", "Electronics", true);

        when(deviceRepository.save(device)).thenReturn(device);

        Device result = deviceService.createDevice(device);

        assertEquals("Laptop", result.getName());
    }

    @Test
    void testFindDeviceById() {
        Device device = new Device(1L, "Laptop", "Electronics", true);

        when(deviceRepository.findById(1L)).thenReturn(Optional.of(device));

        Device result = deviceService.getDeviceById(1L);

        assertEquals("Laptop", result.getName());
    }
}

 

We used mock objects to replace dependencies and verified that the service operates correctly.


Conclusion

In this article, we explored how to use Mockito in Spring Boot, implemented the business logic for a REST API, and tested it using @Mock and @InjectMocks. This approach allows us to create isolated and efficient unit tests, improving application reliability.

Now you can apply Mockito in your projects and test your code more effectively! 🚀