When working with applications that use Spring Boot, you will need to write Unit Test for your code. In this tutorial, I will show you how to write Unit Test for the Spring Boot application!
First, I will create a simple Spring Boot application for example:
This application just does a simple job of printing a message to the console with the content “Hello Khanh. Total of 2 and 3 is 5”. To do this, our application will have 2 classes declared in the Spring container with the following content:
Hello class:
1 2 3 4 5 6 7 8 9 10 11 |
package com.huongdanjava.springboot; import org.springframework.stereotype.Component; @Component public class Hello { public String say(String name) { return "Hello " + name; } } |
This class defines a method that allows us to pass a name and it will return the string “Hello” plus the name you passed.
Calculation class:
1 2 3 4 5 6 7 8 9 10 11 |
package com.huongdanjava.springboot; import org.springframework.stereotype.Service; @Service public class Calculation { public int sum(int a, int b) { return a + b; } } |
This class allows us to calculate the sum of 2 numbers.
The result when running this program will be as follows:
Ok, now we will proceed to write Unit Test code for this application!
The idea is that we need to write Unit Tests to test when the application runs, the two beans of Hello and Calculation classes must be initialized in the Spring container and their methods must return the values we want.
To do this, first you need to make sure the dependency of the Spring Boot Test is declared in your pom.xml file. When I created the Spring Boot project using the Spring Tool Suite, it was included as follows:
1 2 3 4 5 6 7 8 9 10 11 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> |
You may wonder why, when creating the Spring Boot project by default, this Spring Boot Starter Test excludes the junit-vintage-engine library. The reason is because junit-vintage-engine is for JUnit 4 and JUnit 3, we should use the latest version of JUnit as JUnit 5. With dependency declaration for JUnit 5 as follows:
1 2 3 4 5 |
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> |
To write Unit Test for the Spring Boot application, you just need to declare in the Test class an annotation of Spring Boot Test @SpringBootTest. For example:
1 2 3 4 5 6 7 8 |
package com.huongdanjava.springboot; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class HelloTest { } |
Or:
1 2 3 4 5 6 7 8 |
package com.huongdanjava.springboot; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class CalculationTest { } |
The SpringBootUnitTestApplicationTests class has been automatically added:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.springboot; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SpringBootUnitTestApplicationTests { @Test void contextLoads() { } } |
With this @SpringBootTest annotation, the Spring Boot Test will automatically run a Spring container and initialize beans in this Spring container while we run the test.
Bean of which class will be initialized in Spring container depending on our configuration.
If you declare the @ContextConfiguration annotation with value as classes containing bean information definition, only the beans defined in these classes will be initialized.
If you do not declare the @ContextConfiguration annotation, the Spring Boot Test will automatically scan for classes that are declared with the @Configuration annotation and used with the @SpringBootConfiguration annotation to initialize the bean.
In my example, I am using auto component scan to initialize beans in Spring, do not explicitly declare beans, so these beans are used with the @SpringBootConfiguration annotation to initialize.
You can add code tests to test the beans of classes Hello and Calculation must be initialized in SpringBootUnitTestApplicationTests class as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.huongdanjava.springboot; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; @SpringBootTest class SpringBootUnitTestApplicationTests { @Autowired private ApplicationContext context; @Test public void testCalculation() { Assertions.assertTrue(context.getBean(Calculation.class) != null); } @Test public void testHello() { Assertions.assertTrue(context.getBean(Hello.class) != null); } } |
Test for Hello and Calculation classes will be as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.huongdanjava.springboot; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; @SpringBootTest public class CalculationTest { @Autowired private ApplicationContext context; @Test public void testSum() { Calculation calculation = context.getBean(Calculation.class); Assertions.assertEquals(9, calculation.sum(4, 5)); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.huongdanjava.springboot; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; @SpringBootTest public class HelloTest { @Autowired private ApplicationContext context; @Test public void testSay() { Hello hello = context.getBean(Hello.class); Assertions.assertEquals("Hello Khanh", hello.say("Khanh")); } } |
If you only want to test the Hello class, you can create a new TestConfiguration class with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.springboot; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TestConfiguration { @Bean public Hello hello() { return new Hello(); } } |
Then use the @ContextConfiguration annotation to declare this TestConfiguration class in to write tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.huongdanjava.springboot; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = { TestConfiguration.class }) public class Hello1Test { @Autowired private ApplicationContext context; @Test public void testSay() { Hello hello = context.getBean(Hello.class); Assertions.assertEquals("Hello Khanh", hello.say("Khanh")); } } |
In this case, you won’t be able to get the bean of the Calculation class. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.huongdanjava.springboot; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = { TestConfiguration.class }) public class Hello1Test { @Autowired private ApplicationContext context; @Test public void testSay() { Hello hello = context.getBean(Hello.class); Assertions.assertEquals("Hello Khanh", hello.say("Khanh")); } @Test public void testBeanConfiguration() { Calculation calculation = context.getBean(Calculation.class); } } |
The error will happen immediately: