Mock beans and spy beans

When running tests, you sometimes need to mock certain components in the application context. For example, you might have a facade for some remote service that is not accessible during development. Mocking can also be useful if you need to simulate crashes that are difficult to cause in a real environment.

Spring Boot contains a @MockBean annotation that can be used to define a Mockito mock for a bean inside ApplicationContext. You can use an annotation to add new beans or replace one existing bean definition. The annotation can be used directly on test classes, on fields within a test, or on classes and fields with the @Configuration annotation. When used with a field, an instance of the created mock object is also injected. Mock beans are automatically reset after each test method is executed.

If the test uses one of Spring Boot's test annotations (for example, @SpringBootTest), this feature is automatically enabled. To use this feature with another organization type, you must explicitly add listeners, as shown in the following example:

Java

import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@ContextConfiguration(classes = MyConfig.class)
@TestExecutionListeners({ MockitoTestExecutionListener.class, ResetMocksTestExecutionListener.class })
class MyTests {
    // ...
}
Kotlin

import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.TestExecutionListeners
@ContextConfiguration(classes = [MyConfig::class])
@TestExecutionListeners(
    MockitoTestExecutionListener::class,
    ResetMocksTestExecutionListener::class
)
class MyTests {
    // ...
}

The following example replaces an existing RemoteService bean with a mock implementation:

Java

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@SpringBootTest
class MyTests {
    @Autowired
    private Reverser reverser;
    @MockBean
    private RemoteService remoteService;
    @Test
    void exampleTest() {
        given(this.remoteService.getValue()).willReturn("spring");
        String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
        assertThat(reverse).isEqualTo("gnirps");
    }
}
Kotlin

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
@SpringBootTest
class MyTests(@Autowired val reverser: Reverser, @MockBean val remoteService: RemoteService) {
    @Test
    fun exampleTest() {
        given(remoteService.value).willReturn("spring")
        val reverse = reverser.reverseValue // Calls injected RemoteService
        assertThat(reverse).isEqualTo("gnirps")
    }
}
-The @MockBean annotation cannot be used to mock logic the bean that runs when the application context is updated. By the time the test is executed, the update of the application context will have already been completed, and it will be too late to configure the simulated operating logic. In this situation, we recommend using a method marked with the @Bean annotation to create and configure the mock object.

Alternatively, you can use the @SpyBean annotation to wrap any existing bean with a spy object from Mockito.

Proxies from CGLib, such as those created for beans included in accessibility scope, declare proxied methods as final. This stops the Mockito framework from working correctly because it cannot mock or follow final methods in its default configuration. If you need to mock or monitor such a bean, configure Mockito to use an inline mock object constructor by adding org.mockito:mockito-inline to your application's test dependencies. This allows Mockito to mock and follow final methods.
Although the Spring testing framework caches application contexts between tests and reuses the context for tests with the same configuration, using the @MockBean or @SpyBean annotations affects the cache key, which will likely increase the number of contexts.
If you use the @SpyBean annotation to keep track of beans with @Cacheable annotated methods that reference parameters by name, your application must be compiled with the -parameters option. This way, the caching infrastructure is guaranteed to have access to the parameter names once the bean is discovered.
If the annotation is @SpyBean used to monitor the bean being proxied in Spring, in some situations it may be necessary to remove the Spring proxy, for example, when setting expected events using given or when. This is done using AopTestUtils.getTargetObject(yourProxiedSpy).

Auto-configuring tests

Spring Boot's auto-configuration system works great with applications, but can sometimes be too redundant for tests. It is often practical to load only those parts of the configuration that are needed to test the "layer" of the application. For example, you might want to ensure that Spring MVC controllers render URLs correctly, but don't want to involve database calls in those tests, or you might want to test JPA entities, but the web tier isn't interested in running those tests. .

The spring-boot-test-autoconfigure module contains a number of annotations that can be used to automatically configure such "layers". Each of them works the same way, representing an annotation of the form @...​Test, which loads ApplicationContext, and one or more annotations of the form @AutoConfigure...​ , which can be used to configure auto-configuration parameters.

Each layer restricts component scanning to the relevant components and loads a very limited set of auto-configuration classes. If you need to exclude one of them, most annotations of the form @...​Test provide an attribute excludeAutoConfiguration. As an alternative, you can use the @ImportAutoConfiguration#exclude annotation.
Enabling multiple “layers” using multiple annotations of the form @…​Test into one test are not supported. If multiple "layers" are required, select one of the @...​Test annotations and add @AutoConfigure...​ annotations to the other "layers" manually.
You can also use annotations like @AutoConfigure...​ with the standard annotation @SpringBootTest. You can use this combination if you are not interested in creating layers of your application, but you require some of the auto-configured test beans.

Auto-configured JSON tests

To ensure that JSON serialization and deserialization -objects works as expected, you can use the @JsonTest annotation. The @JsonTest annotation automatically configures an available supported JSON mapper, which can be one of the following libraries:

  • ObjectMapper from Jackson, any beans with the @JsonComponent annotation and any Modules from Jackson

  • Gson

  • Jsonb

List of autoconfigurations, activated by the @JsonTest annotation can be found in the documentation appendix.

If you need to configure autoconfig elements, you can use the @AutoConfigureJsonTesters annotation.

Spring Boot contains AssertJ-based helper classes that work with the JSONAssert and JsonPath libraries, designed to check that the intended JSON mapping is correct. The classes JacksonTester, GsonTester, JsonbTester and BasicJsonTester can be used for the Jackson, Gson, Jsonb and Strings libraries, respectively. Any auxiliary fields of the test class can be marked with the @Autowired annotation when using the @JsonTest annotation. The following example shows a test class for the Jackson library:

Java

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import static org.assertj.core.api.Assertions.assertThat;
@JsonTest
class MyJsonTests {
    @Autowired
    private JacksonTester<VehicleDetails> json;
    @Test
    void serialize() throws Exception {
        VehicleDetails details = new VehicleDetails("Honda", "Civic");
        // Assertion for a ".json" file in the same package as the test
        assertThat(this.json.write(details)).isEqualToJson("expected.json");
        // Or use assertions based on the JSON path
        assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
        assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
    }
    @Test
    void deserialize() throws Exception {
        String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
        assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
        assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
    }
}
Kotlin

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.json.JsonTest
import org.springframework.boot.test.json.JacksonTester
@JsonTest
class MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) {
    @Test
    fun serialize() {
        val details = VehicleDetails("Honda", "Civic")
        // Assertion for a ".json" file in the same package as the test
        assertThat(json.write(details)).isEqualToJson("expected.json")
        // Or use assertions based on JSON path
        assertThat(json.write(details)).hasJsonPathStringValue("@.make")
        assertThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda")
    }
    @Test
    fun deserialize() {
        val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"
        assertThat(json.parse(content)).isEqualTo(VehicleDetails("Ford", "Focus"))
        assertThat(json.parseObject(content).make).isEqualTo("Ford")
    }
}
JSON helper classes can also be used directly in standard unit tests. To do this, call the initFields method of the helper class in your method annotated with @Before if the @JsonTest annotation is not used.

If you use the AssertJ-based Spring Boot helper classes to add an assertion for a number value in a given JSON path, you may not need to use isEqualTo depending on the type. Instead, you can use the satisfies function in AssertJ to add an assertion that a value satisfies a given condition. For example, the following example adds a statement that the actual number is a float value close to 0.15 within offset 0.01.

Java

@Test
void someTest() throws Exception {
    SomeObject value = new SomeObject(0.152f);
    assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
            .satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}
Kotlin

            @Test
fun someTest() {
    val value = SomeObject(0.152f)
    assertThat(json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
        .satisfies(ThrowingConsumer { number ->
            assertThat(number.toFloat()).isCloseTo(0.15f, within(0.01f))
        })
}

Spring MVC auto-configurable tests

To test Spring MVC controllers for intended functionality, use the @WebMvcTest annotation. The @WebMvcTest annotation automatically configures the Spring MVC framework and limits scanned beans to @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, WebMvcRegistrations and HandlerMethodArgumentResolver. Regular beans with @Component and @ConfigurationProperties annotations are not scanned when using the @WebMvcTest annotation. To add beans marked with the @ConfigurationProperties annotation, you can use the @EnableConfigurationProperties annotation.

If you need to register additional components, such as Module from Jackson, you can import additional configuration classes using the @Import annotation in your test.

Often the @WebMvcTest is limited to a single controller and is used in conjunction with the @MockBean annotation to pass mock implementations for the required communicating objects.

@WebMvcTest also automatically configures MockMvc. Mock MVC provides an efficient way to quickly test MVC controllers without having to run a full HTTP server.

You can also automatically configure MockMvc without the @WebMvcTest annotation (and for example, with the @SpringBootTest annotation), annotating it with @AutoConfigureMockMvc. The following example uses MockMvc:
Java

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
    @Autowired
    private MockMvc mvc;
    @MockBean
    private UserVehicleService userVehicleService;
    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot"))
            .willReturn(new VehicleDetails("Honda", "Civic"));
        this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andExpect(content().string("Honda Civic"));
    }
}
Kotlin

import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvc) {
    @MockBean
    lateinit var userVehicleService: UserVehicleService
    @Test
    fun testExample() {
        given(userVehicleService.getVehicleDetails("sboot"))
            .willReturn(VehicleDetails("Honda", "Civic"))
        mvc.perform(MockMvcRequestBuilders.get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
            .andExpect(MockMvcResultMatchers.status().isOk)
            .andExpect(MockMvcResultMatchers.content().string("Honda Civic"))
    }
}
If you need to configure auto-configuration elements (for example, if you need to apply servlet filters), you can use the attributes in the @AutoConfigureMockMvc annotation .

If you are using HtmlUnit and Selenium, auto-configuration also provides a WebClient bean for HtmlUnit and/or a WebDriver bean for Selenium. The following example uses HtmlUnit:

Java

import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {
    @Autowired
    private WebClient webClient;
    @MockBean
    private UserVehicleService userVehicleService;
    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
        HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
        assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
    }
}
Kotlin

import com.gargoylesoftware.htmlunit.WebClient
import com.gargoylesoftware.htmlunit.html.HtmlPage
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {
    @MockBean
    lateinit var userVehicleService: UserVehicleService
    @Test
    fun testExample() {
        given(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic"))
        val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html")
        assertThat(page.body.textContent).isEqualTo("Honda Civic")
    }
}
By default, Spring Boots places WebDriver into a special "availability area" to ensure that the driver exits after each test and a new instance is deployed. If this operating logic is unnecessary, you can add the @Scope("singleton") annotation to the definition of your WebDriver with the @Bean annotation.
A webDriver accessibility scope created by Spring Boot will replace any user-defined accessibility scope with the same name. If you have your own webDriver accessibility scope defined, you may find that it stops working when you use the @WebMvcTest annotation.

If the classpath has the Spring Security annotation @WebMvcTest will also scan the WebSecurityConfigurer beans. Instead of completely disabling security for such tests, you can use the test support from Spring Security.

Sometimes writing tests in Spring MVC is not enough; Spring Boot can help you run full-featured end-to-end tests using a real server.

Spring WebFlux Self-Configuring Tests

To ensure that Spring WebFlux controllers work as expected, you can use the @WebFluxTest annotation. The @WebFluxTest annotation automatically configures the Spring WebFlux framework and limits scanned beans to @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, WebFilter and WebFluxConfigurer. Regular beans with @Component and @ConfigurationProperties annotations are not scanned when using the @WebFluxTest annotation. To add beans marked with the @ConfigurationProperties annotation, you can use the @EnableConfigurationProperties annotation.

If You need to register additional components such as Module from the Jackson library, you can import additional configuration classes using the @Import annotation in the test.

Often the @WebFluxTest is limited to a single controller and is used in conjunction with the @MockBean annotation to pass mock implementations for the required communicating objects.

The @WebFluxTest annotation code> also automatically configures WebTestClient, which provides an efficient means of quickly testing WebFlux controllers without the need to run a full HTTP server.

You can also automatically configure WebTestClient without the @WebFluxTest annotation (and for example with the @SpringBootTest annotation) by annotating it with @AutoConfigureWebTestClient. The following example shows a class that uses the @WebFluxTest and WebTestClient annotation:
Java

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.mockito.BDDMockito.given;
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
    @Autowired
    private WebTestClient webClient;
    @MockBean
    private UserVehicleService userVehicleService;
    @Test
    void testExample() {
        given(this.userVehicleService.getVehicleDetails("sboot"))
            .willReturn(new VehicleDetails("Honda", "Civic"));
        this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Honda Civic");
    }
}
Kotlin

import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@WebFluxTest(UserVehicleController::class)
class MyControllerTests(@Autowired val webClient: WebTestClient) {
    @MockBean
    lateinit var userVehicleService: UserVehicleService
    @Test
    fun testExample() {
        given(userVehicleService.getVehicleDetails("sboot"))
            .willReturn(VehicleDetails("Honda", "Civic"))
        webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
            .expectStatus().isOk
            .expectBody<String>().isEqualTo("Honda Civic")
    }
}
This configuration is only supported by WebFlux applications, since using WebTestClient in a simulated web application currently only works in WebFlux.
The @WebFluxTest annotation cannot detect routes registered with the web functional framework. To test RouterFunction beans in context, consider importing RouterFunction yourself using the @Import annotation or using the @SpringBootTest annotation .
The @WebFluxTest annotation cannot detect a custom security configuration registered as a @Bean type SecurityWebFilterChain. To include it in your test, you need to import the configuration that registers the bean using the @Import annotation or the @SpringBootTest annotation.
Sometimes writing tests in Spring WebFlux is not enough; Spring Boot can help you run full-featured end-to-end tests using a real server.