How to Unit Test a Spring Boot Application

Learn to write unit and integration tests in spring boot applications. Learn the difference between unit tests and integration tests along with annotations that support such tests.

See Also: Spring Boot Tests for Controller, Service and Dao Layers

1. Difference between Unit Test and Integration Test

Typically any software application is divided into different modules and components. When one such component is tested in isolation, it is called unit testing. It is written to verify that a relatively small piece of code is doing what it is intended to do.

Unit tests do not verify whether the application code works with external dependencies correctly. It focuses on a single component and mocks all dependencies this component interacts with.

Once different modules are developed and integrated then Integration testing is carried out. Its main purpose is to discover the issues when different modules interact with each other to process user requests end to end.

Integration tests can put the whole application in scope or only certain components – based on what is being tested. They may need to require resources like database instances and hardware to be allocated for them. However, these interactions can be mocked out as well to improve the test performance.

In terms of typical Spring boot crud application, unit tests can be written to test REST controllers, DAO layer etc separately. It will not require even the embedded server as well.

In integration testing, we shall focus on testing complete request processing from the controller to the persistence layer. The application shall run inside the embedded server to create an application context and all beans. Some of these beans may be overridden to mock certain behaviors.

2. Maven

Before Spring Boot 2.2.6.RELEASEspring-boot-starter-test included Junit 4 dependency transitively. Spring Boot 2.3.0 onwards, Junit Jupiter is included instead.

To write tests in spring boot applications, the best way is to include spring-boot-starter-test in pom.xml file. It brings Junit, AssertJ, Hamcrest, Mockito, JSONassert and JsonPath dependencies into the application with the test scope.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

3. Initializing the Tests

Tests written in spring boot can be run in a variety of ways. Let’s see a few most common ways.

3.1. @RunWith(SpringRunner.class) – [ Junit 4 ]

By default, tests written are on Junit 4. To run such tests, we can use SpringRunner class (extends SpringJUnit4ClassRunner) with @RunWith annotation at the class level.

@RunWith(SpringRunner.class)
@WebFluxTest(controllers = EmployeeController.class)
public class EmployeeRestControllerTest {

  //tests
}

3.2. @RunWith(MockitoJUnitRunner.class) – [ Junit 4 with Mockito ]

If tests use @Mock objects then prefer using MockitoJUnitRunner. It initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.

@RunWith(MockitoJUnitRunner.class)
public class EmployeeRestControllerTest {

  @Mock
  private Repository repository;
}

3.3. @ExtendWith(SpringExtension.class) – [ Junit 5 ]

The SpringExtension integrates the Spring TestContext Framework into JUnit 5’s Jupiter programming model.

//@ExtendWith(SpringExtension.class)  // included in @WebFluxTest
@WebFluxTest(controllers = EmployeeController.class)
@Import(EmployeeService.class)
public class EmployeeControllerTest {

  // tests
}

3.4. @ExtendWith(MockitoExtension.class) – [ Junit 5 and Mockito ]

MockitoExtension initializes mocks and handles strict stubbings. It is equivalent of the MockitoJUnitRunner.

Most test annotations include this annotation with them so no need to include it explicitly.

@ExtendWith(MockitoExtension.class)
public class EmployeeControllerTest {

  // tests
}

4. Spring Boot *Test Annotations

Spring boot provides various annotations to enable test infrastructure related to only certain parts of the application. It also provides annotations that help in integration testing as well. Let’s visit them.

4.1. @SpringBootTest

This annotation helps in writing integration tests. It starts the embedded server and fully initializes the application context. We can inject the dependencies in test class using @Autowired annotation.

We can also provide test specific beans configuration using nested @Configuration class or explicit @TestConfiguration classes.

It also provides support for different web environment modes and running web server listening on a defined or random port. It also registers a TestRestTemplate and/or WebTestClient bean for use in web tests.

@SpringBootTest(classes = SpringBootDemoApplication.class, 
    webEnvironment = WebEnvironment.RANDOM_PORT)
public class EmployeeControllerIntegrationTests {

  @LocalServerPort
  private int port;
 
  @Autowired
  private TestRestTemplate testRestTemplate;
 
  //tests
}

Read More : @SpringBootTest example

4.2. @WebMvcTest

This annotation is used for Spring MVC tests. It disables full auto-configuration and instead applies only configuration relevant to MVC tests.

It also auto-configures MockMvc instance as well. We can initialize only one web controller by passing .class as the annotation attribute.

@WebMvcTest(EmployeeRESTController.class)
public class TestEmployeeRESTController {
  
  @Autowired
  private MockMvc mvc;
  
  // tests
}

Read More : @WebMvcTest example

4.3. @WebFluxTest

This annotation disables full auto-configuration and instead applies only configuration relevant to WebFlux tests. By default, tests annotated with @WebFluxTest will also auto-configure a WebTestClient.

Typically @WebFluxTest is used in combination with @MockBean or @Import to create any collaborators required by the controller bean.

@WebFluxTest(controllers = EmployeeController.class)
@Import(EmployeeService.class)
public class EmployeeControllerTest {

  @MockBean
  EmployeeRepository repository;
 
  @Autowired
  private WebTestClient webClient;
 
  //tests
}

Read More : @WebFluxTest example

4.4. Other frequently used annotations

  • @JdbcTest – can be used for a typical JDBC test when a test focuses only on jdbc-based components. It disables full auto-configuration and instead applies only configuration relevant to JDBC tests. By default, tests annotated with @JdbcTest are transactional and roll back at the end of each test. The annotation configures an in-memory embedded database and JdbcTemplate.
  • @JooqTest – It can be used when a test focuses only on jOOQ-based components. Beware that by default, tests annotated with @JooqTest use the application-configured database. To use an embedded in-memory database, @AutoConfigureTestDatabase annotation can be used to override these settings.
  • @JsonTest – It is used when a test focuses only on JSON serialization. It initializes the @JsonComponent, JacksonTester, JsonbTester and GsonTester fields.
  • @DataJpaTest – It can be used to test JPA applications. By default, it scans for @Entity classes and configures Spring Data JPA repositories. If an embedded database is available on the classpath, it configures one as well.

    By default, data JPA tests are transactional and roll back at the end of each test. Data JPA tests may also inject a TestEntityManager bean, which provides an alternative to the standard JPA EntityManager that is specifically designed for tests.

  • @DataMongoTest – is used to test MongoDB applications. By default, it configures an in-memory embedded MongoDB (if available), configures a MongoTemplate, scans for @Document classes, and configures Spring Data MongoDB repositories.
  • @DataRedisTest – is used to test Redis applications. By default, it scans for @RedisHash classes and configures Spring Data Redis repositories.
  • @DataLdapTest – is used to test LDAP applications. By default, it configures an in-memory embedded LDAP (if available), configures a LdapTemplate, scans for @Entry classes, and configures Spring Data LDAP repositories.
  • @RestClientTest – is used to test REST clients. By default, it auto-configures Jackson, GSON, and Jsonb support, configures a RestTemplateBuilder, and adds support for MockRestServiceServer.

5. @TestConfiguration

The @TestConfiguration is specialized form of @Configuration that can be used to define additional beans or customizations for a test.

In spring boot, any beans configured in a top-level class annotated with @TestConfiguration will not be picked up via component scanning. We must explicitly register the @TestConfiguration class with the class that contains the test cases.

The best thing is that these test configurations are not automatically part of the application’s primary configuration. They are available only on-demand using one of below two ways to include this additional test configuration i.e.

5.1. @Import Annotation

It can be used to import one or more configuration classes into the application context or spring test context.

@TestConfiguration
public class AppTestConfiguration {

    @Bean
    DataSource inMemoryDataSource(){
    	//...
    }
}
@Import(AppTestConfiguration.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringBootDemoApplicationTests
{  
    @LocalServerPort
    int randomServerPort;
  
    @Autowired
    DataSource datasource;
  
    //tests
}

5.2. Static Nested Classes

We can define the test configurations in nested classes inside the test class. The nested class can be annotated with @Configuration or @TestConfiguration annotations.

  • In case of nested @Configuration class, the given configuration would be used “instead of” the application’s primary configuration.
  • A nested @TestConfiguration class is used “in addition to” the application’s primary configuration.
public class AppTests
{
    @Autowired
    DataSource datasource;

    //tests

    @TestConfiguration
		static class AppTestConfiguration {

		    @Bean
		    DataSource inMemoryDataSource(){
		    	//
		    }
		}
}

6. Mocking

Spring boot has excellent support for mocking the dependencies with or without using Mockito.

6.1. With @Mock

To process Mockito annotations with JUnit, we need to use MockitoExtention which automatically initializes all the objects annotated with @Mock and @InjectMocks annotations.

@ExtendWith(MockitoExtension.class)
public class TestEmployeeManager {

	@InjectMocks
	EmployeeManager manager;

	@Mock
	EmployeeDao dao;

	//tests
}

6.2. Without @MockBean

@MockBean annotation used to add mocks to a Spring ApplicationContext. It allows one to mock a class or an interface and to record and verify behaviors on it.

Interestingly, any existing bean of the same type defined in the context will be replaced by the mock. If no existing bean is defined a new one will be added.

@MockBean is similar to mockito’s @Mock but with Spring support. We will generally use @MockBean along with either @WebMvcTest or @WebFluxTest annotations. These annotations are for web test slices and are limited to a single controller.

In the given example, we are mocking the EmployeeRepository bean. In this way, all the application code will be invoked but all repository interactions will be mocked.

@WebFluxTest(controllers = EmployeeController.class)
@Import(EmployeeService.class)
public class EmployeeControllerTest
{
    @MockBean
    EmployeeRepository repository;

    @Autowired
    private WebTestClient webClient;

    //tests
}

7. Conclusion

Spring boot provides excellent support for unit testing and integration testing of applications and it’s various modules. We shall use the provided support through the use of annotations – very carefully.

Use @SpringBootTest annotation for integration testing while other auto-configuration annotations for unit testing of specific components.

Mocking the certain behavior is very common requirement and we can use either mockito’s @Mock or Spring’s @MockBean annotation for this purpose.

Drop me your questions in the comments section.

Happy Learning !!

Sourcecode in Github

Comments

Subscribe
Notify of
guest
2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.