Icon for gfsd IntelliJ IDEA

How to do mocking in Spring Boot? Best practices for using @MockBean vs @Mock

Read Symflower's guide to mocking in Spring Boot

In this post, we’ll cover the basics of mocking objects when unit testing Spring Boot applications with examples!

What is mocking?

In the context of unit testing, mocking is used to replace the external dependencies with “mocked” objects that simulate the behavior of real objects. The advantage of mocking is that it lets you test isolated “units” (e.g. components) of your application. Mocking lets you rule out any dependencies interfering with how the tested unit works, since mocked objects will always work the way you configured them. Mocking also lets you test independently of the status of those external dependencies, e.g. other components that are still being developed.

🤔 Wondering which mocking framework to use?

Check out our post: Mocking frameworks for Java: Mockito vs EasyMock vs JMockit

Mocking in Spring Boot with Mockito & JUnit

Mocking in Spring Boot is usually done with a combination of Mockito and JUnit, so that’s what we’ll cover in the following sections.

When unit testing Spring Boot applications, there are three* options to create mocks. It’s important to know the differences since you have to use each one of them differently.

*)Well actually, there are four. You could just use Symflower, which supports mocking out of the box! See an example at the bottom of this post.

Three ways to create mocks in Spring Boot: @MockBean, @Mock, or Mockito.mock()?

Using the @MockBean annotation in Spring Boot

The most straightforward option is just to use @MockBean, the most simple way to mock an object in Spring Boot.

@MockBean is a Spring Boot annotation that is provided by the Spring Boot Test module. Use @MockBean to define mocks for beans inside your ApplicationContext, including beans auto-configured by Spring Boot, and beans that you have declared in your application’s configuration file.

@MockBean lets you add new beans: if a bean definition with the same type already exists in the application context, it will simply be replaced with a mock. A great thing about @MockBean is that you can use it on test classes, fields within your test, or @Configuration classes or fields.

Note that mocked beans will be automatically reset after each test method and that when you use @MockBean on a field, Spring Boot will automatically take care of injecting the instance of the created mock.

Example: using @MockBean:

@SpringBootTest
public class UserServiceIntegrationTest {
    @Autowired
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Test
    public void testGetUserById() {
        // Given
        User mockedUser = new User("John", "Doe", 25);
        when(userRepository.findById(1L)).thenReturn(Optional.of(mockedUser));

        // When
        User result = userService.getUserById(1L);

        // Then
        assertNotNull(result);
        assertEquals("John", result.getFirstName());
        assertEquals("Doe", result.getLastName());
        assertEquals(25, result.getAge());
        verify(userRepository).findById(1L); // Verify that the findById method was called
    }
}

Source: https://www.java67.com/2023/04/difference-between-mockitomock-mock-and.html

Using the @Mock annotation in Spring Boot

There’s some chance you’re already familiar with @Mock as it is also used in plain Java (e.g. non-Spring) applications. @Mock is actually a shorthand for Mockito.mock() (see below), so it’s recommended that you use it for cleaner code.

This annotation is provided by the Mockito library and is useful when you’re using dependencies that are not part of your application’s Spring context. @Mock lets you mock these objects using Mockito very simply.

Note that you can only use @Mock within test classes. It’s a good idea to use it if you want to reuse the mocked object and avoid calling the mock() method several times. @Mock lets you specify a name for the mocked object which improves code readability and facilitates finding errors. Another neat feature of @Mock is that it also allows you to use @InjectMocks to inject a mock object into another mock object.

To use @Mock, you’ll need to enable the use of Mockito annotations either by using MockitoJUnitRunner or by calling the MockitoAnnotations.initMocks() method. To initialize mock objects, use the MockitoJUnitRunner or the MockitoExtension classes.

  • Note that MockitoJUnitRunner is JUnit 4-specific, and is used in a class-level annotation: @RunWith(MockitoJUnitRunner.class)!
  • MockitoExtension, on the other hand, is the JUnit 5-specific version that is also used in a class-level annotation: @ExtendWith(MockitoExtension.class).

Example: using @Mock:

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetUserById() {
        // Given
        User mockedUser = new User("John", "Doe", 25);
        when(userRepository.findById(1L)).thenReturn(Optional.of(mockedUser));
        
        // When
        User result = userService.getUserById(1L);

        // Then
        assertNotNull(result);
        assertEquals("John", result.getFirstName());
        assertEquals("Doe", result.getLastName());
        assertEquals(25, result.getAge());
        verify(userRepository).findById(1L); // Verify that the findById method was called
    }
}

Source: https://www.java67.com/2023/04/difference-between-mockitomock-mock-and.html

Using Mockito.mock() in Spring Boot

An easy one here: Mockito.mock() is actually the same thing as @Mock. Like @Mock, you can use it to create a mock object of classes and interfaces, and it lets you create mock class fields and local mocks in methods.

However, @Mock is considered a cleaner solution and is generally recommended over Mockito.mock(). Other advantages of using @Mock over Mockito.mock() are that you can inject mocks created with @Mock into the class you’re testing using @InjectMocks. Also, if there is an error with your mock, the name of the mock annotated with @Mock will be shown in the error messages so it’s easier to identify the problem. For those reasons (and for easily readable code), we recommend using @Mock over Mockito.mock().

In case you do need to use it, here’s an example of how to do it!

Example: Mockito.mock():

// Example interface
public interface GreetingService {
    String greet(String name);
}

// Test case using Mockito.mock()
@Test
public void testGreet() {
    // Create a mock object of MyService
    GreetingService myServiceMock = Mockito.mock(GeetingService.class);

    // Define behavior for the greet() method
    Mockito.when(myServiceMock.greet(Mockito.anyString())).thenReturn("Hello, ");

    // Test the method that depends on MyService
    MyClass myClass = new MyClass(myServiceMock);
    String result = myClass.greet("John");

    // Verify the expected behavior
    Mockito.verify(myServiceMock).greet("John");
    Assert.assertEquals("Hello, John", result);
}

Source: https://www.java67.com/2023/04/difference-between-mockitomock-mock-and.html

Comparing the @MockBean vs @Mock annotations in Spring Boot: which one to use?

So which method should you use for mocking in Spring Boot? Here are a few things to bear in mind:

  • Use @Mock for plain JUnit tests with Mockito. @Mock is not aware of your Spring context, and you should rely on it when testing isolated components of your application.
  • @MockBean is aware of the application context and is therefore used for Spring Boot tests to mock dependencies in a Spring application context, including beans annotated with @Service, @Repository, or @Component.
  • Remember the different imports: @Mock should be imported from the org.mockito package, while @MockBean resides in the org.springframework.boot.test.mock.mockito package.
  • You’ll need to manually inject mock objects annotated with @Mock to the test instance. You can do this using @InjectMocks or by calling MockitoAnnotations.initMocks(...) in your test setup. @MockBean takes care of that automatically by replacing the actual bean with the mock in your Spring context.

+1: Mocking for Spring MVC web applications

When working on Spring MVC web applications, the Spring MVC Test framework (MockMVC) can be used to manage request handling with mock request and response objects.

😳 Spring Web MVC or Spring WebFlux?

Read our comparison of Spring Web MVC and Spring WebFlux.

So if you are developing RESTful applications, it is worth taking a look at the capabilities that MockMvc provides. To sum it all up, you can test all HTTP requests such as PUT/GET/POST requests including support for URI and request parameters.

For our example, let’s assume we have a BookController class that offers a GET mapping on the URI /books/most-popular. In this case, the test using MockMvc might look as follows:

@RunWith(SpringRunner.class)
@WebMvcTest(BookController.class)
public class BookControllerTest {
   @Autowired
   private MockMvc mockMvc;

   ...

   @Test
   public void getMostPopularBookByISBN() throws Exception {
       this.mockMvc.perform(get("/books/most-popular"))
           .andExpect(status().isOk())
           .andExpect(view().name(""))
           .andExpect(content().string(""));
   }
}

While it’s not rocket science, it can certainly take a bit of time to write the above code for every class being tested. It would be great to save & spend all that time on building actual functionality, right? That’s exactly what Symflower is for! We used Symflower to generate the test code above (e.g. the test for the BookController class) – all it took was a single click in our editor.

Integrated into your IDE, Symflower can generate test templates with mocks right where you do your coding without disrupting your workflow. And with the new code lens feature, it really only takes one click – see how it works in action:

Make sure you never miss any of our upcoming content by signing up for our newsletter and by following us on Twitter, LinkedIn or Facebook!

| 2024-03-06