Original address: www.baeldung.com/spring-boot…

1 overview

In this tutorial, we’ll show you how to write test cases using the framework in Spring Boot. The content covers unit tests, as well as integration tests that launch the Spring context before executing test cases. If you are new to Spring Boot, check out the link: Introduction to Spring Boot.

Explore Spring Boot TestRestTemplate, Spring Boot @RestClientTest for quick navigation, and inject Mockito Mocks into Spring Beans

2 Project Startup

The application we are going to use is an API that provides some basic operations (add, delete, change, and query) on the Employee table. This is a typical layered framework — API calls go from the Controller layer to the Service layer and finally to the persistence layer.

3 the Maven rely on

First add test dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <version>2.2.6. RELEASE</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>
Copy the code

The spring-boot-starter-test package is the primary dependency that contains most of the elements required for testing. The H2 database is an in-memory database. It doesn’t require us to configure and start a real database, so it makes it easier for developers below the test scenario.

3.1 takeup

In Spring Boot 2.4, JUnit 5’s Vintage Engine package has been removed from spring-boot-starter-test. If we want to write test cases using JUnit4, we need to add the following dependencies.

<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>
Copy the code

4 Perform integration tests on @Springboottest

As the title says, integration testing focuses on integrating different layers of the application (controller layer, Service layer, and persistence layer). This also means there is no mocking involved.

Ideally, unit tests should be separated from integration tests and should not be run with unit tests. We can achieve this separation by using different configuration files. Why do you do that? Because typical integration tests are time consuming and may require a real database (not an in-memory database) to perform.

In this article, however, we won’t focus on that, we’ll focus on using an in-memory database for H2 persistent storage.

Integration testing requires starting a container to execute test cases. So a few extra Settings are required — all of which are easy to do in Spring Boot.

@RunWith(SpringRunner.class)
@SpringBootTest( SpringBootTest.WebEnvironment.MOCK, classes = Application.class)
@AutoConfigureMockMvc
@TestPropertySource( locations = "classpath:application-integrationtest.properties")
public class EmployeeRestControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private EmployeeRepository repository;

    // write test cases here
}
Copy the code

The @SpringBoottest annotation is useful when we need to start the entire container. This annotation creates the ApplicationContext needed in the test case.

We can configure the runtime environment with the webEnvironment attribute annotated by @SpringBooTtest; We can use webEnvironment. MOCK here so that the entire container will run in a simulated servlet environment.

The @testpropertysource annotation then helps us configure the configuration file address to use in our test case. Note that the annotation configuration configuration file overrides the existing application.properties configuration file.

Application – integrationtest. Properties stored in the configuration file contains the persistence layer configuration details:

spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
Copy the code

If we want to use MySQL for integration testing, we can modify the configuration files (application – integrationtest. Properties) values. Test cases for integration tests look like unit tests for the Controller layer.

@Test
public void givenEmployees_whenGetEmployees_thenStatus200(a)
  throws Exception {

    createTestEmployee("bob");

    mvc.perform(get("/api/employees")
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(content()
      .contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
      .andExpect(jsonPath("$[0].name", is("bob")));
}
Copy the code

The difference is that in the Controller layer test case, nothing is simulated and an end-to-end scenario is executed.

5 through@TestConfigurationTest configuration

As we saw earlier, the class with the @SpringbooTtest annotation kicks off the entire application context, which means that we can inject any classes that are scanned by Component into our test class via @Autowire:

@RunWith(SpringRunner.class)
@SpringBootTest
public class EmployeeServiceImplIntegrationTest {

    @Autowired
    private EmployeeService employeeService;

    // class code ...
}
Copy the code

However, we might want to avoid starting the entire application and just start a particular test configuration. We can do this with the @TestConfiguration annotation. There are two ways to use this annotation. One way is that we can use this annotation in the inner class to inject classes that we want to inject via @Autowire.

@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {
        @Bean
        public EmployeeService employeeService(a) {
            return new EmployeeService() {
                // implement methods}; }}@Autowired
    private EmployeeService employeeService;
}
Copy the code

Alternatively, we can create separate test configuration classes instead of inner classes:

@TestConfiguration
public class EmployeeServiceImplTestContextConfiguration {
    
    @Bean
    public EmployeeService employeeService(a) {
        return new EmployeeService() { 
            // implement methods }; }}Copy the code

Configuration classes annotated with @TestConfiguration are excluded by Componet scanning, so we need to explicitly import the class in all the test classes we want to use @AutoWired. We can do this with the @import annotation:

@RunWith(SpringRunner.class)
@Import(EmployeeServiceImplTestContextConfiguration.class)
public class EmployeeServiceImplIntegrationTest {

    @Autowired
    private EmployeeService employeeService;

    // remaining class code
}
Copy the code

6 Use @MockBean to simulate

The Service layer code depends on the persistence layer code:

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Override
    public Employee getEmployeeByName(String name) {
        returnemployeeRepository.findByName(name); }}Copy the code

However, when testing the Service layer, we don’t need or care how the persistence layer is implemented. Ideally, we should be able to write and test Service layer code without linking the full persistence layer code.

To achieve this decoupling, we can use Spring Boot Test’s Mocking support to do this.

Let’s take a look at the framework of the test class:

@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {
 
        @Bean
        public EmployeeService employeeService(a) {
            return newEmployeeServiceImpl(); }}@Autowired
    private EmployeeService employeeService;

    @MockBean
    private EmployeeRepository employeeRepository;

    // write test cases here
}
Copy the code

To check the Service class, we need to have an instance of the Service class already created and available via @Bean so that we can inject the Service class into the test class via @AutoWired. We can do this with the @testConfiguration annotation.

Another interesting thing here is the use of @MockBean. It creates an EmployeeRepository impersonator class that can be used to replace the real EmployeeRepository.

@Before
public void setUp(a) {
    Employee alex = new Employee("alex");

    Mockito.when(employeeRepository.findByName(alex.getName()))
      .thenReturn(alex);
}
Copy the code

Once started, the test case is simple:

@Test
public void whenValidName_thenEmployeeShouldBeFound(a) {
    String name = "alex";
    Employee found = employeeService.getEmployeeByName(name);
 
     assertThat(found.getName())
      .isEqualTo(name);
 }
Copy the code

7 through@DataJpaTestAnnotation integration testing

We will use the Employee entity, which has two attributes: ID and name:

@Entity
@Table(name = "person")
public class Employee {

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

    @Size(min = 3, max = 20)
    private String name;

    // standard getters and setters, constructors
}
Copy the code

Here is the persistence layer class using Spring Data JPA:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee.Long> {

    public Employee findByName(String name);

}
Copy the code

This is the persistence layer code. Now let’s move on to writing the test code. First, we create the basic framework for the test class:

@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryIntegrationTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private EmployeeRepository employeeRepository;

    // write test cases here

}
Copy the code

The @runwith (springrunner.class) annotation provides a bridge between the Spring Boot Test feature and JUnit. This annotation is useful when we need to use Spring Boot test features in JUnit test classes.

The @DatajPatest annotation provides some standard Settings for the persistence layer test class:

  • Configure the H2 database, an in-memory database
  • Set up Hibernate, SPring Data, and DataSource
  • Perform @ EntityScan
  • Open SQL logging

To continue with the database operation, we need to add some records to the database. To set this data, we can use TestEntityManager.

Spring Boot TestEntityManager is an alternative to the standard JPA EntityManager, which provides common methods for writing tests.

EmployeeRepository is the component we want to test. Now let’s write our first test case;

@Test
public void whenFindByName_thenReturnEmployee(a) {
    // given
    Employee alex = new Employee("alex");
    entityManager.persist(alex);
    entityManager.flush();

    // when
    Employee found = employeeRepository.findByName(alex.getName());

    // then
    assertThat(found.getName())
      .isEqualTo(alex.getName());
}
Copy the code

In the above test case, we insert an Employee record into the database through TestEntityManager and then read the record through the named API. AssertThat comes from the Assertj library, which is bundled with Spring Boot.

8 through@WebMvcTestUnit testing

The Controller layer depends on the Service layer. For simplicity, let’s add a simple method:

@RestController
@RequestMapping("/api")
public class EmployeeRestController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public List<Employee> getAllEmployees(a) {
        returnemployeeService.getAllEmployees(); }}Copy the code

Since we are only focusing on the Controller layer code, naturally we can simulate the Service layer in our unit tests:

@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private EmployeeService service;

    // write test cases here
}
Copy the code

To test the Controller layer, we can use @webMvcTest to only launch a single Controller class in most cases. We can use this with the @MockBean annotation to provide any mock implementation we need to rely on. It will automatically configure the Spring MVC infrastructure for our unit tests.

In most cases, @webMvcTest will only launch a single Controller class. We can use this with the @MockBean annotation to provide any mock implementation we need to rely on.

MockMvc is automatically configured by @webMvcTest, which provides a powerful way to simplify the way to test the MVC Controller layer without having to start a full HTTP server.

The test classes are as follows:

@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray(a)
  throws Exception {
    
    Employee alex = new Employee("alex");

    List<Employee> allEmployees = Arrays.asList(alex);

    given(service.getAllEmployees()).willReturn(allEmployees);

    mvc.perform(get("/api/employees")
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(jsonPath("$", hasSize(1)))
      .andExpect(jsonPath("$[0].name", is(alex.getName())));
}
Copy the code

Get () method calls can be replaced with other HTTP equivalents, such as PUT (), POST (), and so on. Note that we also set the content type in the request. MockMvc is flexible and can be used to create any request.

9 Automatic configuration test

One of the amazing features of Spring Boot’s auto-configuration annotations is that they help load certain parts of the full application and specific test layers of the code base.

In addition to the annotations provided above, here is a list of widely used annotations:

  • WebFluxTest: We can use it@WebFluxTestAnnotations to test the Spring WebFlux controller. It is often@MockBeanUsed together to provide mock implementations for the required dependencies.
  • JdbcTest: We can use it@JdbcTestAnnotations to test JPA applications, but only for tests that only require data sources. This annotation configures an in-memory embedded database and aJdbcTemplate.
  • @JooqTest
  • @DataMongoTest

.

You can read more about these annotations and continue to tune your integration tests, tuning your Spring integration tests.

10 the conclusion

In this article, we delve into testing in Spring Boot and show how to write test cases more effectively.

The source code for all of this article can be found here, Github. The source code contains many other examples and different test cases.

Guide to Testing With the Spring Boot Starter Test rieckpil.de/ Guide-to-te…