Spring Boot provides a number of utilities and annotations that make it easier to test your application. Testing support is provided by two modules: spring-boot-test contains the core elements, and spring-boot-test-autoconfigure supports autoconfiguration for tests.

Most developers use the "starter" spring-boot-starter-test, which imports both Spring Boot test modules, as well as JUnit Jupiter, AssertJ, Hamcrest and a number of other useful libraries.

If you have tests that use JUnit 4, you can use the vintage JUnit 5 engine to run them. To use the vintage engine, add a dependency on junit-vintage-engine, as shown in the following example:


<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

hamcrest-core dropped in favor of org.hamcrest:hamcrest, which is part of spring-boot-starter-test.

Availability Test Area Dependencies

"starter" spring-boot-starter-test (in test scope) contains the following provided libraries:

  • JUnit 5: The de facto standard for unit testing of Java applications.

  • Spring Test and Spring Boot Test: Tools to support utilities and integration tests for Spring Boot applications.

  • AssertJ: Fluid assertion library.

  • Hamcrest: Library of matchers (also known as constraints or predicates) .

  • Mockito: Java framework for mocking )

  • JSONassert: JSON assertion library.

  • JsonPath: XPath for JSON.

We usually find these common libraries useful when writing tests. If these libraries do not meet your needs, you can add additional test dependencies as you wish.

Testing Spring Applications

One of the main benefits of dependency injection is that it should make it easier to modularize code testing. It is possible to instantiate objects using the new operator without even involving Spring. You can also use mock objectsinstead of real dependencies.

Often you want to go beyond unit testing and start integrating testing (with ApplicationContext from Spring). It is useful to be able to perform integration testing without necessarily deploying the application or connecting to other infrastructure.

Spring Framework includes a special test module for such integration testing. You can declare a dependency directly on org.springframework:spring-test or use the "starter" spring-boot-starter-test for transitive connection.

Testing Spring Boot Applications

A Spring Boot application is an ApplicationContext for Spring, so nothing special is required to test it other than what you do for a vanilla Spring context.

External properties, logging, and other Spring Boot features are installed in the default context only if you use SpringApplication to create it.

Spring Boot provides a @SpringBootTest annotation that can be used as an alternative to the standard @ContextConfiguration annotation for spring-test when the need arises in Spring Boot features. The annotation creates an ApplicationContext used in tests via SpringApplication. In addition to the @SpringBootTest annotation, there are also a number of other annotations for testing more specific slices of the application.

If you are using JUnit 4 , don't forget to also add @RunWith(SpringRunner.class) to the test, otherwise the annotations will be ignored. If you are using JUnit 5, there is no need to add the equivalent of the @ExtendWith(SpringExtension.class) annotation, since the @SpringBootTest annotation and the other @…​Test are already annotated with it.

By default, the @SpringBootTest annotation does not start the server. You can use the webEnvironment attribute for the @SpringBootTest annotation to further refine the progress of your tests:

  • MOCK(default) : Loads the web context ApplicationContext and provides a mock web environment object. Embedded servers do not start when using this annotation. If the web environment is not available in your classpath, this mode transparently falls back to creating a regular non-web ApplicationContext. It can be used in combination with the @AutoConfigureMockMvc or @AutoConfigureWebTestClient annotation for mock-based testing of a web application.

  • RANDOM_PORT: Loads the WebServerApplicationContext and provides the actual web environment. Embedded servers start and listen on a random port.

  • DEFINED_PORT: Loads the WebServerApplicationContext and provides the actual web environment. Embedded servers start and listen on the specified port (from the application.properties file) or the default port 8080.

  • NONE: Loads ApplicationContext using SpringApplication, but does not provide any web environment (mock or otherwise).

If your test is marked with the @Transactional annotation, it by default rolls back the transaction at the end of each test method . However, since using this scheme with RANDOM_PORT or DEFINED_PORT implicitly exposes the actual servlet environment, the HTTP client and server run in separate threads and thus in separate transactions. Any transaction initiated on the server is not rolled back in this case.
The @SpringBootTest annotation with webEnvironment = WebEnvironment .RANDOM_PORT also starts the management server on a separate random port if the application uses a different port for the management server.

Defining the type of web application

If Spring MVC is available, the normal one is configured MVC based application context. If only Spring WebFlux is present, this will be determined and the application context will be configured based on WebFlux.

If both frameworks are present, Spring MVC takes precedence. If you want to test a reactive web application in this scenario, you need to set the spring.main.web-application-type property:

Java

import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
    // ...
} 
Kotlin

import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {
    // ...
}

Defining a test configuration

If you're familiar with the Spring Test Framework, you're probably used to using @ContextConfiguration(classes=…​) to specify which @Configuration for Spring should be loaded. Alternatively, you could often use nested classes with the @Configuration annotation in your test.

When testing Spring Boot applications, this is usually not required. The @*Test annotations in Spring Boot search for your primary configuration automatically if you have not explicitly defined one.

The search algorithm starts from the package containing the test until it finds the class , annotated with @SpringBootApplication or @SpringBootConfiguration. Provided you have structured your code adequately, the basic configuration can usually be found.

If you are using a test annotation to test a more specific layer of the application , then you should avoid adding domain-specific configuration options in the main method's application class.

The basic component scanning configuration, which includes the @SpringBootApplication annotation, defines the exclusion filters that are used to ensure that the slice acquisition works as expected. If you use an explicit directive with the @ComponentScan annotation on a class marked with the @SpringBootApplication annotation, be aware that these filters will be disabled. If you resort to obtaining slices, you should define them again.

If you need to configure the primary configuration, you can use a nested class marked with the @TestConfiguration annotation. Unlike a nested class marked with the @Configuration annotation, which is used in place of the application's main configuration, a nested class with the @TestConfiguration annotation is used in addition to the application's main configuration.

The Spring testing framework caches application contexts between tests. Therefore, as long as the tests use the same configuration (regardless of how it was discovered), the potentially resource-intensive process of loading the context occurs only once.

Test configuration exception

If the application uses component scanning (for example, when using the @SpringBootApplication or @ComponentScan annotations), you may find that high-level configuration classes that were created only for certain tests are accidentally intercepted throughout.

The @TestConfiguration annotation can be used in an internal test class to configure the primary configuration. When the @TestConfiguration annotation marks a high-level class, it specifies that classes in src/test/java should not be intercepted during scanning. You can then import this class explicitly when needed, as shown in the following example:

Java

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
    @Test
    void exampleTest() {
        // ...
    }
}
Kotlin

import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import
@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {
    @Test
    fun exampleTest() {
        // ...
    }
}
If The @ComponentScan annotation is used directly (that is, not through the @SpringBootApplication annotation), you must register the TypeExcludeFilter with it.

Using Application Arguments

If your application accepts arguments, you can use the @SpringBootTest annotation to inject them through the args attribute.

Java

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {
    @Test
    void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
        assertThat(args.getOptionNames()).containsOnly("app.test");
        assertThat(args.getOptionValues("app.test")).containsOnly("one");
    }
}
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.ApplicationArguments
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {
    @Test
    fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
        assertThat(args.optionNames).containsOnly("app.test")
        assertThat(args.getOptionValues("app.test")).containsOnly("one")
    }
}

Testing with a mock environment

By default, the @SpringBootTest annotation does not start the server, but instead creates a mock environment for testing end webs -points.

In Spring MVC, you can query web endpoints using MockMvc or WebTestClient, as shown in the following example:

Java

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
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;
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
    @Test
    void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
        mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
    }
    // If Spring WebFlux is in the classpath, then you can manage MVC tests using WebTestClient
    @Test
    void testWithWebTestClient(@Autowired WebTestClient webClient) {
        webClient
                .get().uri("/")
                .exchange()
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo("Hello World");
    }
}
Kotlin

import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
    @Test
    fun testWithMockMvc(@Autowired mvc: MockMvc) {
        mvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.status().isOk)
            .andExpect(MockMvcResultMatchers.content().string("Hello World"))
    }
    // If Spring WebFlux is in the classpath, then you can manage MVC tests using WebTestClient
    @Test
    fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk
            .expectBody<String>().isEqualTo("Hello World")
    }
}
If you need to focus only on the web layer and not run the full ApplicationContext, then consider using the @WebMvcTest annotation as an alternative.

You can use WebTestClient with Spring WebFlux endpoints as shown in the following example:

Java

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }
}
Kotlin

import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
    @Test
    fun exampleTest(@Autowired webClient: WebTestClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk
            .expectBody<String>().isEqualTo("Hello World")
    }
}

Testing in a simulation environment is usually faster than when using a populated servlet container. However, because mocking occurs at the Spring MVC level, code that relies on lower-level servlet container logic cannot be directly tested with MockMvc.

For example, Spring Boot's error handling relies on " error pages" provided by the servlet container. This means that while it is possible to verify that the MVC layer throws and handles exceptions as intended, it is not possible to directly verify that a particular custom error page is rendered. If you need to test these low-level nuances, you can run a fully functioning server, as described in the next section.

Testing with a running server

If you want to run a fully functioning server, then We recommend using random ports. If you use @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT), the available port is chosen randomly each time the test is run.

The @LocalServerPort annotation can be used to in order to inject the actually used port into the test. For convenience, tests that need to make REST calls to a running server can optionally bind a WebTestClient via the @Autowire annotation, which allows relative references to the running server and has a special API for testing responses, as shown in the following example:

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.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }
}
Kotlin

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.context.SpringBootTest.WebEnvironment
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
    @Test
    fun exampleTest(@Autowired webClient: WebTestClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk
            .expectBody<String>().isEqualTo("Hello World")
    }
}
WebTestClient you can use for both real servers and mock environments.

This configuration requires spring-webflux to be present in the classpath. If you can't or don't want to add webflux, Spring Boot also provides the ability to use TestRestTemplate:

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.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/", String.class);
        assertThat(body).isEqualTo("Hello World");
    }
}
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.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.client.TestRestTemplate
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
    @Test
    fun exampleTest(@Autowired restTemplate: TestRestTemplate) {
        val body = restTemplate.getForObject("/", String::class.java)
        assertThat(body).isEqualTo("Hello World")
    }
}

Configuring WebTestClient

To configure the WebTestClient bean, configure the WebTestClientBuilderCustomizer bean . Any such beans are called using the WebTestClient.Builder, which is used to create the WebTestClient.

Using JMX

Because the test context framework caches context, JMX technology is disabled by default to prevent identical beans from being registered in the same domain. If such a test requires access to MBeanServer, consider marking it as dirty:

Java

import javax.management.MBeanServer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
    @Autowired
    private MBeanServer mBeanServer;
    @Test
    void exampleTest() {
        assertThat(this.mBeanServer.getDomains()).contains("java.lang");
        // ...
    }
}
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.context.SpringBootTest
import org.springframework.test.annotation.DirtiesContext
import javax.management.MBeanServer
@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {
    @Test
    fun exampleTest() {
        assertThat(mBeanServer.domains).contains("java.lang")
        // ...
    }
}

Using Metrics

Regardless of the classpath, counter registries other than those stored in memory are not automatically configured when using the @SpringBootTest annotation.

If you need to export metrics to another backend as part of an integration test, annotate it with @AutoConfigureMetrics.