Now that we have completed the first round of development of our lead microservice, it is time to focus on some testing. I will follow a bottom-up approach and start by writing unit tests for the repository layer.
In order to write unit tests, I will make use of the Spring Test Framework and Spring Boot Test support. Spring Boot makes it extremely easy to unit test slices of the application. We can use @DataJpaTest and just unit test the database layer with an embedded database like H2. However, there are some gotchas (I do not know exactly what at this moment), the @DataJpaTest does not work if you have extended the Spring Data JPA to add a custom method for all the repositories. The problem I found was that the when running the Spring Boot application, everything works fine and the custom method works fine and the query returns the correct results. However, when the unit test is run, Spring Data JPA fails, complaining that it is not able to find an attribute for the entity/type.
So to write unit tests for the repository of the lead microservice, I will use the standard @SpringBootTest annotation. The goal here is to unit test and hence only beans relevant to the repository layer are created. To achieve this we will have to prevent unnecessary beans from being created - for example, the controller beans etc. Remember unit tests have to be cheap and must run fast. This will ensure quick feedback and promote continuous delivery principles. Note that the repository unit tests are created and run in an embedded database and hence they are very fast.
Listing 1 - below shows the initial version of the repository unit test class.
There are few important items to note in Listing 1.
Finally, some changes are required in the build.gradle dependency management section to include the spring-test-dbunit jars in the lead microservice project. This is shown in snippet below.
In order to write unit tests, I will make use of the Spring Test Framework and Spring Boot Test support. Spring Boot makes it extremely easy to unit test slices of the application. We can use @DataJpaTest and just unit test the database layer with an embedded database like H2. However, there are some gotchas (I do not know exactly what at this moment), the @DataJpaTest does not work if you have extended the Spring Data JPA to add a custom method for all the repositories. The problem I found was that the when running the Spring Boot application, everything works fine and the custom method works fine and the query returns the correct results. However, when the unit test is run, Spring Data JPA fails, complaining that it is not able to find an attribute for the entity/type.
So to write unit tests for the repository of the lead microservice, I will use the standard @SpringBootTest annotation. The goal here is to unit test and hence only beans relevant to the repository layer are created. To achieve this we will have to prevent unnecessary beans from being created - for example, the controller beans etc. Remember unit tests have to be cheap and must run fast. This will ensure quick feedback and promote continuous delivery principles. Note that the repository unit tests are created and run in an embedded database and hence they are very fast.
Listing 1 - below shows the initial version of the repository unit test class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.effectiv.crm.repository.ut; | |
import static org.junit.Assert.*; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; | |
import org.springframework.test.context.TestExecutionListeners; | |
import org.springframework.test.context.junit4.SpringRunner; | |
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; | |
import org.springframework.test.context.support.DirtiesContextTestExecutionListener; | |
import org.springframework.test.context.transaction.TransactionalTestExecutionListener; | |
import org.springframework.transaction.annotation.Transactional; | |
import com.effectiv.crm.domain.Lead; | |
import com.effectiv.crm.repository.LeadRepository; | |
import com.github.springtestdbunit.DbUnitTestExecutionListener; | |
import com.github.springtestdbunit.annotation.DatabaseSetup; | |
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; | |
/** | |
* Lead Repository Unit test | |
* This test cannot be run with @DataJpaTest due to issues | |
* with Spring Data JPA custom repository method | |
* @author Dhrubo | |
* | |
*/ | |
@SpringBootTest(webEnvironment=WebEnvironment.NONE) | |
@Transactional | |
@RunWith(SpringRunner.class) | |
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, | |
TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class }) | |
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2) | |
//@WithMockUser(username = "test@t.com", roles = { "ADMIN", "USER" }) | |
@DatabaseSetup("lead.xml") | |
//TODO WithSecurityContextTestExecutionListener.class to be added in future to include security in this test | |
public class LeadRepositoryTest { | |
@Autowired | |
private LeadRepository repository; | |
@Test | |
public void findOne() { | |
Lead persistedLead = this.repository.findOne("1"); | |
assertEquals("1",persistedLead.getId()); | |
assertEquals("Virat",persistedLead.getFirstName()); | |
} | |
//@Test | |
public void findOneDoesNotExist() { | |
//TODO - Need to provide implementation | |
} | |
//@Test | |
public void findAll() { | |
} | |
//@Test | |
public void findAllError() { | |
} | |
//@Test | |
public void save() { | |
} | |
//@Test | |
public void delete() { | |
} | |
@Test | |
public void update() { | |
} | |
//@Test | |
public void purge() { | |
} | |
} |
- Most of the methods are not yet implemented. They will be implemented along with future posts.
- @Transactional - annotation ensures that the transaction which started at the beginning of the test method is rolled back after the method completes. In other words, the test data that is created is removed.
- @SpringBootTest(webEnvironment=WebEnvironment.NONE) - ensures that web environment is not started in other words no controllers or web layer beans are created ensuring that the test runs fast.
- @DatabaseSetup("lead.xml") - this is an interesting annotation. I have integrated the spring-test-dbunit project to integrate DBUnit utilities with Spring Boot Test. The detailed documentation of this project can be found at this link - https://springtestdbunit.github.io/spring-test-dbunit/
Few words on Spring test db unit
- This project makes it extremely easy to create test data and then clean it up.
- @DatabaseSetup - can be added both at the class and method level. The transaction starts with the DB setup process and it inserts the data in the lead.xml in this example into the database for easy test data setup. Once the test completes and transaction rolls back, this data is removed. When the annotation is added at the class level, then it is run for all the test methods. It can also be added to individual test methods and in that case, the test data will be loaded only for that particular method.
- In order to load the lead.xml file for test data it must be stored under the same package name as the repository class. However, the source folder will be - 'src/test/resources'
- The DbUnitTestExecutionListener will also be required to include Spring-test-db-unit. as shown in the snippet below.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, | |
TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class }) |
![]() |
Figure 1 - Test code and resource folder |
![]() |
Figure 2 - JUnit All Green |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
testCompile('com.github.springtestdbunit:spring-test-dbunit:1.3.0'){ | |
exclude group: 'junit' | |
exclude group : 'org.springframework' | |
} | |
testCompile group: 'org.dbunit', name: 'dbunit', version: '2.5.2' |
Comments
Post a Comment