Learn how to test the different parts of a Spring Boot web application . We will see some very quick examples (using Junit 5 ) and configurations for:
Verifying that the application has been initialized successfully
Unit testing REST Controller with @WebMvcTest
Unit testing Service Layer with Mockito
Unit testing DAO Layer with @DataJpaTest
and @AutoConfigureTestDatabase
Integration testing using @SpringBootTest
System testing using RestTemplate
For demo purposes, we have created a very simple Employee management application. It has a few CRUD API calls for creating, fetching and deleting the employees from the database.
Do not forget to use @ExtendWith(SpringExtension.class)
to initialize and inject the mocks using Mockito in the test classes.
1. Maven
This demo application uses Spring Boot 3 and Java 17. It includes the auto-configuration from the following modules:
spring-boot-starter-web
spring-boot-starter-validation
spring-boot-starter-test
with Junit 5
spring-boot-starter-data-jpa
< dependencies>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-data-jpa</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-validation</ artifactId>
</ dependency>
< dependency>
< groupId> com.h2database</ groupId>
< artifactId> h2</ artifactId>
< scope> runtime</ scope>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-test</ artifactId>
< scope> test</ scope>
</ dependency>
</ dependencies>
2. Testing if Application Bootstraps Correctly
This is the simplest of all. Write a Test class annotated with @SpringBootTest
and check for any important eagerly initialized bean if it has been successfully injected into an auto-wired attribute or not.
mvn test
import org. assertj. core. api. Assertions ;
import org. junit. jupiter. api. Test ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. boot. test. context. SpringBootTest ;
import com. howtodoinjava. employees. controllers. EmployeeController ;
@SpringBootTest
public class EmployeesApplicationTests {
@Autowired
EmployeeController employeeController;
@Test
public void contextLoads ( ) {
Assertions . assertThat ( employeeController) . isNot ( null ) ;
}
}
3. Unit Testing the REST @Controller
Write a Test class annotated with @WebMvcTest
. We can specify which Controller we want to test in the annotation value itself.
@WebMvcTest ( EmployeeController . class )
public class StandaloneControllerTests {
@MockBean
EmployeeService employeeService;
@Autowired
MockMvc mockMvc;
@Test
public void testfindAll ( ) throws Exception {
Employee employee = new Employee ( "Lokesh" , "Gupta" ) ;
List < Employee > employees = Arrays . asList ( employee) ;
Mockito . when ( employeeService. findAll ( ) ) . thenReturn ( employees) ;
mockMvc. perform ( get ( "/employee" ) )
. andExpect ( status ( ) . isOk ( ) )
. andExpect ( jsonPath ( "$" , Matchers . hasSize ( 1 ) ) )
. andExpect ( jsonPath ( "$[0].firstName" , Matchers . is ( "Lokesh" ) ) ) ;
}
}
4. Unit Testing the Service Layer
To unit test the service layer, we must use mock the DAO layer. Then we can run the tests using MockitoExtension
.
@ExtendWith ( MockitoExtension . class )
public class ServiceTests {
@InjectMocks
EmployeeService service;
@Mock
EmployeeRepository dao;
@BeforeEach
public void init ( ) {
MockitoAnnotations . openMocks ( this ) ;
}
@Test
void testFindAllEmployees ( ) {
List < Employee > list = new ArrayList < Employee > ( ) ;
Employee empOne = new Employee ( "John" , "John" ) ;
Employee empTwo = new Employee ( "Alex" , "kolenchiski" ) ;
Employee empThree = new Employee ( "Steve" , "Waugh" ) ;
list. add ( empOne) ;
list. add ( empTwo) ;
list. add ( empThree) ;
when ( dao. findAll ( ) ) . thenReturn ( list) ;
List < Employee > empList = service. findAll ( ) ;
assertEquals ( 3 , empList. size ( ) ) ;
verify ( dao, times ( 1 ) ) . findAll ( ) ;
}
@Test
void testCreateOrSaveEmployee ( ) {
Employee employee = new Employee ( "Lokesh" , "Gupta" ) ;
service. save ( employee) ;
verify ( dao, times ( 1 ) ) . save ( employee) ;
}
}
5. Unit testing DAO / Repository Layer
To unit test the DAO layer, we first need an in-memory test database. This we can achieve using @AutoConfigureTestDatabase .
Then we need to use @DataJpaTest
which disables full auto-configuration and instead apply only configuration relevant to JPA tests.
@DataJpaTest
@AutoConfigureTestDatabase ( replace = AutoConfigureTestDatabase. Replace . NONE)
public class DaoTests {
@Autowired
EmployeeRepository employeeRepository;
@Test
public void testCreateReadDelete ( ) {
Employee employee = new Employee ( "Lokesh" , "Gupta" ) ;
employeeRepository. save ( employee) ;
Iterable < Employee > employees = employeeRepository. findAll ( ) ;
Assertions . assertThat ( employees) . extracting ( Employee :: getFirstName ) . containsOnly ( "Lokesh" ) ;
employeeRepository. deleteAll ( ) ;
Assertions . assertThat ( employeeRepository. findAll ( ) ) . isEmpty ( ) ;
}
}
6. Integration Testing
Integration tests cover the whole path through the application. In these tests, we send a request to the application and check that it responds correctly and has changed the database state according to our expectations.
The database can be an actual physical database or in-memory database for testing purposes.
import javax. validation. ValidationException ;
import org. assertj. core. api. Assertions ;
import org. junit. jupiter. api. Test ;
import org. junit. jupiter. api. extension. ExtendWith ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. boot. test. context. SpringBootTest ;
import org. springframework. test. context. junit. jupiter. SpringExtension ;
import com. howtodoinjava. employees. model. Employee ;
@ExtendWith ( SpringExtension . class )
@SpringBootTest
public class IntegrationTests {
@Autowired
EmployeeController employeeController;
@Test
public void testCreateReadDelete ( ) {
Employee employee = new Employee ( "Lokesh" , "Gupta" ) ;
Employee employeeResult = employeeController. create ( employee) ;
Iterable < Employee > employees = employeeController. read ( ) ;
Assertions . assertThat ( employees) . first ( ) . hasFieldOrPropertyWithValue ( "firstName" , "Lokesh" ) ;
employeeController. delete ( employeeResult. getId ( ) ) ;
Assertions . assertThat ( employeeController. read ( ) ) . isEmpty ( ) ;
}
@Test
public void errorHandlingValidationExceptionThrown ( ) {
Assertions . assertThatExceptionOfType ( ValidationException . class )
. isThrownBy ( ( ) -> employeeController. somethingIsWrong ( ) ) ;
}
}
7. Integration Testing using TestRestTemplate
We can use TestRestTemplate
class to perform system testing. It helps in verifying the application as it looks to the client outside the application.
@SpringBootTest ( webEnvironment = RANDOM_PORT)
public class SystemTests {
@Autowired
private EmployeeController controller;
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testCreateReadDelete ( ) {
String url = "http://localhost:" + port+ "/employee" ;
Employee employee = new Employee ( "Lokesh" , "Gupta" ) ;
ResponseEntity < Employee > entity = restTemplate. postForEntity ( url, employee, Employee . class ) ;
Employee [ ] employees = restTemplate. getForObject ( url, Employee [ ] . class ) ;
Assertions . assertThat ( employees) . extracting ( Employee :: getFirstName ) . containsOnly ( "Lokesh" ) ;
restTemplate. delete ( url + "/" + entity. getBody ( ) . getId ( ) ) ;
Assertions . assertThat ( restTemplate. getForObject ( url, Employee [ ] . class ) ) . isEmpty ( ) ;
}
@Test
public void testErrorHandlingReturnsBadRequest ( ) {
String url = "http://localhost:" + port+ "/wrong" ;
try {
restTemplate. getForEntity ( url, String . class ) ;
} catch ( HttpClientErrorException e) {
Assertions . assertThat ( e. getStatusCode ( ) ) . isEqualTo ( HttpStatus . BAD_REQUEST) ;
}
}
}
8. Conclusion
This Spring boot testing tutorial is to provide a short example of how to configure various dependencies; as well as write various kinds of tests using simple examples.
Feel free to modify the above-given code snippets to your requirements.
Happy Learning !!
Download Source Code
Comments