Writing and running tests is an essential part of delivering reliable software, in this write up I will be documenting my approach to writing effective integration test for web service using spring boot tools. This is going to be lengthy because we will test the persistence layer, service layer, web controllers and even exceptions so grab your cup of coffee.
Before we start integration test with Spring Boot let’s discuss how an integration test is different from a unit test. A unit test targets a small unit of code like a method or a class while an integration test on the other hand is a test that covers multiple units because it tests the interaction between two or more components, the interaction between a web controller, a service layer or a business service, and a persistence layer.
First we will use the Spring initializer to create our maven project with the dependencies listed below.
- Go to → https://start.spring.io/
- Select the following dependencies: Web, JPA, Lombok and H2.
Testing Libraries and Framework
In this article, we will use the following libraries; Java standard testing library JUnit 5, spring boot test support SpringBootTest, WebMvcTest, Mockito, AssertJ fluent API and JSON assets.
To write our integration test we will use the bottom-up approach, Test Driven Development (TDD) and Behavior Driven Development (BDD) process.
Bottom-up testing is an approach to integrated testing where the lowest level components are tested first, then used to facilitate the testing of higher level components. The process is repeated until the component at the top of the hierarchy is tested — definition from wikipedia.org
Test Driven Development (TDD) is a process by which tests are written before the code.
In Behavior Driven Development (BDD), tests are written in plain English describing a set of behaviors as the expected results of a software system. We will use the given, when and then approach to write our tests.
JPA Repository Test
Above, we want to test our persistent layer methods but our test failed. To test the getStudentByName and getAverageAgeForActiveStudents operations we wrote our test cases first because we are practicing TDD and then run it and it failed as expected, we will write the actual implementation to pass the tests. In the testGetStudentByName_returnsStudentDetails and testAvgAgeForActiveStudents_calculateAvgAge methods we use the given, when and then approach because we are also practicing BDD approach. To bring spring boot test support into our application, we could have used @SpringBootTest annotation but it will load the full application context which can make our testing slow and we only want to test the data repository layer so we used @DataJpaTest test slice and the spring TestEntityManager to persist, flush and find.
To pass our tests, we will annotate the student class as an entity and write both the custom JPQL and JPA provided query in our StudentRepository class and retest.
Student Entity Class
Hurray we passed our first tests ! we have successfully tested our repository methods. The next test is the service layer where the business logic is performed.
Service Layer Test
Above, we attempted to test getStudentById method in our service layer and StudentNotFoundException if a missing id is used to fetch student. Our tests failed as expected remember we are practicing TDD, so we will continue to write the actual implementation. Three new classes were introduced to our project, the StudentService class, StudentNotFoundException class and StudentServiceTest class. We loaded the our application with @SpringBootTest and set the WebEnvironment to Non so as just load only what we need and not all the environment and also we used @Transactional to always rollback test data and not persist it in our database.
we have passed our tests again, let proceed to write our last test case for our web controller.
Web Controller Test
We need only @WebMvcTest to fire up all the beans we need to test our controller, we will use the spring @Mockmvc framework to perform http request on our controller endpoint and @MockBean to mock the student service class dependency. To keep it simple we will write only one test.
Our test failed as expected because we have not created the web controller class and expose the getStudentById endpoint to consume. We will go ahead and add the student web controller class and retest.
Student Controller Test
Student Web Controller
Now we can can test again after modifying our classes.
In this article, we took a dive into the testing support in Spring Boot and showed how to write integration tests efficiently for the persistence layer, service layers and the web controller.
The complete source code of this article can be found over on GitHub.