Writing Unit, Integration, and End-to-End Tests With JUnit 5
After we have finished this webinar, we:
- Can create a Maven project that runs unit, integration, and end-to-end tests which use JUnit 5.
- Can use JUnit 5 tags when we are writing unit, integration, and end-to-end tests.
This webinar assumes that:
The Requirements of Our Maven Build
The requirements of our Maven build are:
- Our unit, integration, and end-to-end tests must have separate source and resource directories.
- Only unit tests are run by default.
- We must be able to run only unit tests, integration tests, or end-to-end tests.
- The names of our test classes don’t have to start or end with a special marker string.
Let’s move on and configure the source and resource directories of our integration and end-to-end tests.
Adding Source and Resource Directories to Our Maven Build
As we remember, we can add additional source and resource directories to our Maven build by using the Build Helper Maven Plugin . We can configure this plugin by following these steps:
- Declare the Build Helper Maven plugin in the
pluginssection of our POM file. - Configure the source and resource directories of our integration tests.
- Configure the source and resource directories of our end-to-end tests.
After we have configured the Build Helper Maven plugin, its configuration looks as follows:
1<plugin>
2 <groupId>org.codehaus.mojo</groupId>
3 <artifactId>build-helper-maven-plugin</artifactId>
4 <version>1.12</version>
5 <executions>
6 <execution>
7 <id>add-end-to-end-test-sources</id>
8 <phase>generate-test-sources</phase>
9 <goals>
10 <goal>add-test-source</goal>
11 </goals>
12 <configuration>
13 <sources>
14 <source>src/end-to-end-test/java</source>
15 </sources>
16 </configuration>
17 </execution>
18 <execution>
19 <id>add-end-to-end-test-resources</id>
20 <phase>generate-test-resources</phase>
21 <goals>
22 <goal>add-test-resource</goal>
23 </goals>
24 <configuration>
25 <resources>
26 <resource>
27 <filtering>true</filtering>
28 <directory>src/end-to-end-test/resources</directory>
29 </resource>
30 </resources>
31 </configuration>
32 </execution>
33 <execution>
34 <id>add-integration-test-sources</id>
35 <phase>generate-test-sources</phase>
36 <goals>
37 <goal>add-test-source</goal>
38 </goals>
39 <configuration>
40 <sources>
41 <source>src/integration-test/java</source>
42 </sources>
43 </configuration>
44 </execution>
45 <execution>
46 <id>add-integration-test-resources</id>
47 <phase>generate-test-resources</phase>
48 <goals>
49 <goal>add-test-resource</goal>
50 </goals>
51 <configuration>
52 <resources>
53 <resource>
54 <filtering>true</filtering>
55 <directory>src/integration-test/resources</directory>
56 </resource>
57 </resources>
58 </configuration>
59 </execution>
60 </executions>
61</plugin>
Next, we will find out how we can configure our Maven profiles.
Configuring Our Maven Profiles
We can configure our Maven profiles by following these steps:
First, we have to add three properties to the properties section of our POM file:
- The
junit5.test.tagproperty specifies the tag which identifies the invoked test methods. Because only unit tests are run by default, the default value of this property must beUnitTest. - The
skip.failsafe.pluginproperty specifies if Maven should skip the Maven Failsafe plugin. Because only unit tests are run by default, the default value of this property must betrue. - The
skip.unit.testsproperty specifies if Maven should skip unit tests. Because only unit tests are run by default, the default value of this property must befalse.
After we have added these properties to our POM file, the relevant part of its properties section looks as follows:
1<properties>
2 <junit5.test.tag>UnitTest</failsafe.test.tag>
3 <skip.failsafe.plugin>true</skip.failsafe.plugin>
4 <skip.unit.tests>false</skip.unit.tests>
5</properties>
Second, we have to create the required Maven profiles by following these steps:
- Create a profile called
devand ensure that this profile is active by default. - Create a profile called
end-to-end-testand ensure that this profile runs only our end-to-end tests. - Create a profile called
integration-testand ensure that this profile runs only our integration tests.
After we have configured our Maven profiles, the profiles section of our POM file looks as follows:
1<profiles>
2 <profile>
3 <id>dev</id>
4 <activation>
5 <activeByDefault>true</activeByDefault>
6 </activation>
7 </profile>
8 <profile>
9 <id>end-to-end-test</id>
10 <properties>
11 <junit5.test.tag>EndToEndTest</junit5.test.tag>
12 <skip.failsafe.plugin>false</skip.failsafe.plugin>
13 <skip.unit.tests>true</skip.unit.tests>
14 </properties>
15 </profile>
16 <profile>
17 <id>integration-test</id>
18 <properties>
19 <junit5.test.tag>IntegrationTest</junit5.test.tag>
20 <skip.failsafe.plugin>false</skip.failsafe.plugin>
21 <skip.unit.tests>true</skip.unit.tests>
22 </properties>
23 </profile>
24</profiles>
Let’s move on and find out how we can configure the Maven Surefire plugin.
Configuring the Maven Surefire Plugin
As we remember , we can run our unit tests by using the Maven Surefire plugin. We can configure this plugin by following these steps:
- Ensure that Maven doesn’t run our unit tests if the value of the
skip.unit.testsproperty istrue. - Configure the tag that identifies the test methods which should be run when we run our unit tests. We can do this by setting the value of the
includeTagsconfiguration property. - Configure the dependencies of the Maven Surefire plugin. Because our unit tests use JUnit 5, we have to declate the
junit-platform-surefire-providerandjunit-jupiter-enginedependencies when we configure the dependencies of the Maven Surefire plugin.
After we have configured the Maven Surefire plugin, its configuration looks as follows:
1<plugin>
2 <groupId>org.apache.maven.plugins</groupId>
3 <artifactId>maven-surefire-plugin</artifactId>
4 <version>2.19.1</version>
5 <configuration>
6 <skip>${skip.unit.tests}</skip>
7 <properties>
8 <includeTags>${junit5.test.tag}</includeTags>
9 </properties>
10 </configuration>
11 <dependencies>
12 <dependency>
13 <groupId>org.junit.platform</groupId>
14 <artifactId>junit-platform-surefire-provider</artifactId>
15 <version>1.0.3</version>
16 </dependency>
17 <dependency>
18 <groupId>org.junit.jupiter</groupId>
19 <artifactId>junit-jupiter-engine</artifactId>
20 <version>5.0.3</version>
21 </dependency>
22 </dependencies>
23</plugin>
Next, we will find out how we can configure the Maven Failsafe plugin.
Configuring the Maven Failsafe Plugin
As we remember, we can run both integration and end-to-end tests by using the Maven Failsafe plugin. We can configure the Maven Failsafe plugin by following these steps:
- Ensure that the Maven Failsafe plugin includes all classes whose name ends with the string:
Test. - Ensure that Maven doesn’t run the Maven Failsafe plugin if the value of the
skip.failsafe.pluginproperty istrue. - Configure the tag that identifies the test methods which should be run when Maven runs the Maven Failsafe plugin. We can do this by setting the value of the
includeTagsconfiguration property. - Configure the dependencies of the Maven Surefire plugin. Because our integration and end-to-end tests use JUnit 5, we have to declate the
junit-platform-surefire-providerandjunit-jupiter-enginedependencies when we configure the dependencies of the Maven Failsfafe plugin.
After we have configured the Maven Failsafe plugin, its configuration looks as follows:
1<plugin>
2 <groupId>org.apache.maven.plugins</groupId>
3 <artifactId>maven-failsafe-plugin</artifactId>
4 <version>2.19.1</version>
5 <executions>
6 <execution>
7 <goals>
8 <goal>integration-test</goal>
9 <goal>verify</goal>
10 </goals>
11 </execution>
12 </executions>
13 <configuration>
14 <includes>
15 <include>**/*Test.java</include>
16 </includes>
17 <skip>${skip.failsafe.plugin}</skip>
18 <properties>
19 <includeTags>${junit5.test.tag}</includeTags>
20 </properties>
21 </configuration>
22 <dependencies>
23 <dependency>
24 <groupId>org.junit.platform</groupId>
25 <artifactId>junit-platform-surefire-provider</artifactId>
26 <version>1.0.3</version>
27 </dependency>
28 <dependency>
29 <groupId>org.junit.jupiter</groupId>
30 <artifactId>junit-jupiter-engine</artifactId>
31 <version>5.0.3</version>
32 </dependency>
33 </dependencies>
34</plugin>
We have now configured our Maven project. Let’s move on and find out how we can write simple unit, integration, and end-to-end tests with JUnit 5.
Writing Our Unit, Integration, and End-to-End Tests
JUnit 5 has a concept called a tag that allows us to divide our test methods into different categories that can be used to filter the invoked test methods. When we created our Maven profiles, we also configured the tags that are used to identify our unit, integration, and end-to-end tests.
Because we have to ensure that a test class has at least one tag, it’s good idea to create a constant class which specifies the values of our tags. This constant class helps us to eliminate typos that can happen if we specify these tags by using string literals.
After we have created this constant class, its source code looks as follows:
1public final class TestTags {
2
3 public static final String END_TO_END_TEST = "EndToEndTest";
4 public static final String INTEGRATION_TEST = "IntegrationTest";
5 public static final String UNIT_TEST = "UnitTest";
6
7 private TestTags() {}
8}
Our next step is to write our test classes and configure the category of each test class by using the tags which we created earlier. We can configure the category of a test class or a test method by annotating it with the @Tag annotation. Because we want to ensure that all test methods of a test class belong to a specific category, we have to annotate our test classes with the @Tag annotation.
We can now write our test classes by following these steps:
First, we have to create a class called: UnitTest and ensure that the test methods of this class are run when we run our unit tests. After we have created this class, its source code looks as follows:
1import org.junit.jupiter.api.DisplayName;
2import org.junit.jupiter.api.Tag;
3import org.junit.jupiter.api.Test;
4
5@Tag(TestTags.UNIT_TEST)
6class UnitTest {
7
8 @Test
9 @DisplayName("This test method should be run when we run our unit tests")
10 void shouldBeRunWhenUnitTestsAreRun() {
11 }
12}
Second, we have to create a class called: IntegrationTest and ensure that the test methods of this class are run when we run our integration tests. After we have created this class, its source code looks as follows:
1import org.junit.jupiter.api.DisplayName;
2import org.junit.jupiter.api.Tag;
3import org.junit.jupiter.api.Test;
4
5@Tag(TestTags.INTEGRATION_TEST)
6class IntegrationTest {
7
8 @Test
9 @DisplayName("This test method should be run when we run our integration tests")
10 void shouldBeRunWhenIntegrationTestsAreRun() {
11 }
12}
Third, we have to create a class called: EndToEndTest and ensure that the test methods of this class are run when we run our end-to-end tests. After we have created this class, its source code looks as follows:
1import org.junit.jupiter.api.DisplayName;
2import org.junit.jupiter.api.Tag;
3import org.junit.jupiter.api.Test;
4
5@Tag(TestTags.END_TO_END_TEST)
6class EndToEndTest {
7
8 @Test
9 @DisplayName("This test method should be run when we run our end-to-end tests")
10 void shouldBeRunWhenEndToEndTestsAreRun() {
11 }
12}
Additional Reading:
We have now written the required test classes. Next, we will find out how we can run our unit, integration, and end-to-end tests.
Running Our Unit, Integration, and End-to-End Tests
We can run our unit tests running the command: mvn clean test -P dev at command prompt. When we run this command, we should see that only unit tests are run:
1$ mvn clean test -P dev
2
3-------------------------------------------------------
4 T E S T S
5-------------------------------------------------------
6Running com.testwithspring.master.UnitTest
7Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 sec
8- in com.testwithspring.master.UnitTest
9
10Results :
11
12Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
We can run our integration tests running the command: mvn clean verify -P integration-test at command prompt. When we run this command, we should see that only integration tests are run:
1$ mvn clean verify -P integration-test
2
3-------------------------------------------------------
4 T E S T S
5-------------------------------------------------------
6Running com.testwithspring.master.IntegrationTest
7Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.003 sec
8- in com.testwithspring.master.IntegrationTest
9
10Results :
11
12Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
We can run our end-to-end tests running the command: mvn clean verify -P end-to-end-test at command prompt. When we run this command, we should see that only end-to-end tests are run:
1$ mvn clean verify -P end-to-end-test
2
3-------------------------------------------------------
4 T E S T S
5-------------------------------------------------------
6Running com.testwithspring.master.EndToEndTest
7Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 sec
8- in com.testwithspring.master.EndToEndTest
9
10Results :
11
12Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Let’s summarize what we learned from this webinar.
Summary
This webinar has taught us three things:
- If we want to configure the tag that identifies the test methods which are run by the Maven Surefire or the Maven Failsafe plugin, we can set the value of the
includeTagsconfiguration property. - It’s a good idea to create a constant class which specifies the values of our tags.
- We can configure the category of a test class or a test method by using the
@Tagannotation.