This post was originally published in programmingtechie.com
Junit 5 is one of the popular testing frameworks in the Java Development World and it supports the latest features of Java 8.
If you are a visual learner like me, check out the below video tutorial:
Table of Contents
- Junit 5 Architecture
- Source Code for Project under Test
- Writing our First Test
- Testing Exceptions using assertThrows()
- Understanding Test Lifecycle
- Conditional Executions
- Assumptions
- Repeated Tests
- Parameterized Tests
- Disabled Tests
- Nested Tests
- Running Tests using Maven Surefire Plugin
- Conclusion
Even though JUnit 5 is a successor for Junit 4, the architecture of the framework is completely different, so let’s have a look at the Architecture of Junit 5.
Junit 5 Architecture
The Architecture of Junit 5 can be divided into 3 different projects:
- Junit Platform
- Junit Jupiter
- Junit Vintage
Junit Platform
The Junit Platform project provides an API to launch the tests from either the IDE’s, Build Tools or Console. It defines a TestEngine
API which enables development of new Testing Frameworks on top of the Platform.
Junit Jupiter
This project provides API to write our Junit tests and extensions, it contains new annotations and a TestEngine
implementation to run these tests on the platform.
Junit Vintage
This project provides a TestEngine
implementation to support backward compatibility for tests written with Junit 3 and Junit4.
You can check the architecture Diagram below:
Source Code for Project under Test
We are going to use a Contact Manager Application while learning how to write tests using Junit 5.
You can download the source code of the application from the below Github Link:
https://github.com/SaiUpadhyayula/contact-manager
Source Code for Starter Project
If you want to follow along this tutorial, have a look at the Starter Repository which is in the same state as the start of the tutorial.
https://github.com/SaiUpadhyayula/contact-manager-starter
The Application uses Maven to manage the dependencies and build the project. If you want to learn Maven, you can check out my Complete Maven Tutorial blog post. The application mainly contains 2 classes, Contact.java
and ContactManager.java
Contact.java
public class Contact {
private String firstName;
private String lastName;
private String phoneNumber;
public Contact(String firstName, String lastName, String phoneNumber) {
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public void validateFirstName() {
if (this.firstName == null)
throw new RuntimeException("First Name Cannot be null");
}
public void validateLastName() {
if (this.lastName == null)
throw new RuntimeException("Last Name Cannot be null");
}
public void validatePhoneNumber() {
if (this.phoneNumber.length() != 10) {
throw new RuntimeException("Phone Number Should be 10 Digits Long");
}
if (!this.phoneNumber.matches("\\d+")) {
throw new RuntimeException("Phone Number Contain only digits");
}
if (!this.phoneNumber.startsWith("0")) {
throw new RuntimeException("Phone Number Should Start with 0");
}
}
}
This class just contains 3 necessary fields: First Name, Last Name and Phone Number.
ContactManager.java
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ContactManager {
Map<String, Contact> contactList = new ConcurrentHashMap<String, Contact>();
public void addContact(String firstName, String lastName, String phoneNumber) {
Contact contact = new Contact(firstName, lastName, phoneNumber);
validateContact(contact);
checkIfContactAlreadyExist(contact);
contactList.put(generateKey(contact), contact);
}
public Collection<Contact> getAllContacts() {
return contactList.values();
}
private void checkIfContactAlreadyExist(Contact contact) {
if (contactList.containsKey(generateKey(contact)))
throw new RuntimeException("Contact Already Exists");
}
private void validateContact(Contact contact) {
contact.validateFirstName();
contact.validateLastName();
contact.validatePhoneNumber();
}
private String generateKey(Contact contact) {
return String.format("%s-%s", contact.getFirstName(), contact.getLastName());
}
}
This class contains the main business logic to maintain contact information.
Writing our First Test
Now it is time to write our first Junit 5 Test.
I am going to create a test class called as ContactManagerTest.java
under the src/test/java
folder.
ContactManagerTest.java
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class ContactManagerTest {
@Test
@DisplayName("Should Create Contact")
public void shouldCreateContact() {
ContactManager contactManager = new ContactManager();
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
}
- We created a method called
shouldCreateContact()
which is the method testing, the contact creation. Junit understands that this method is a Test, by looking at the@Test
annotation. - By looking at the method name
shouldCreateContact()
we understand what the method is trying to do, but we can also provide a custom name for this method using the@DisplayName
annotation. - Inside the method, we created a contact by providing the first name, last name and phone number details to the
addContact()
method of theContactManager
class. - We can retrieve all the contact information through the
getAllContacts()
method. - As a last step, we are verifying that the result of
getAllContacts()
is not empty, and the size of thegetAllContacts()
List is exactly 1.
The process of verifying the actual output with the expected output is called as performing assertions.
Junit 5 provides many different methods to perform assertions for different cases.
We have Assertions class which provides number of static methods, we can use in our tests.
If you run the above test in IntelliJ, the test will be green. Congratulations, you wrote your first Junit 5 test.
You can find more details about this class in the documentation - https://junit.org/junit5/docs/5.0.1/api/org/junit/jupiter/api/Assertions.html
Testing Exceptions using assertThrows()
In our first test case, we successfully tested the Happy Path.
Now let's explore some other scenarios.
Here are some validations which are performed when creating a Contact:
- First Name should not be null
- Last Name should not be null
- Phone Number Should :
- Be Exactly 10 Digits Long
- Contain only Digits
- Start with 0
Let's write test cases to test some of the above cases, we will cover all the scenarios in the upcoming sections.
ContactManagerTest.java
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class ContactManagerTest {
@Test
@DisplayName("Should Create Contact")
public void shouldCreateContact() {
ContactManager contactManager = new ContactManager();
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
@Test
@DisplayName("Should Not Create Contact When First Name is Null")
public void shouldThrowRuntimeExceptionWhenFirstNameIsNull() {
ContactManager contactManager = new ContactManager();
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact(null, "Doe", "0123456789");
});
}
}
- The 2nd Test Case, tests whether the Contact Creation is Failed when we enter the First Name as Null.
- We are asserting that the
addContact()
method throws aRuntimeException
- We can assert the Exceptions using the
assertThrow()
method from the Assertions class - The
assertThrow()
method takes the Exception Type as the first parameter and the Executable which is throws the Exception as the second parameter.
We can write similar test cases also for the fields Last Name and Phone Number like below:
ContactManagerTest.java
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class ContactManagerTest {
@Test
@DisplayName("Should Create Contact")
public void shouldCreateContact() {
ContactManager contactManager = new ContactManager();
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
@Test
@DisplayName("Should Not Create Contact When First Name is Null")
public void shouldThrowRuntimeExceptionWhenFirstNameIsNull() {
ContactManager contactManager = new ContactManager();
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact(null, "Doe", "0123456789");
});
}
@Test
@DisplayName("Should Not Create Contact When Last Name is Null")
public void shouldThrowRuntimeExceptionWhenLastNameIsNull() {
ContactManager contactManager = new ContactManager();
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact("John", null, "0123456789");
});
}
@Test
@DisplayName("Should Not Create Contact When Phone Number is Null")
public void shouldThrowRuntimeExceptionWhenPhoneNumberIsNull() {
ContactManager contactManager = new ContactManager();
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact("John", "Doe", null);
});
}
}
You can observe that I added similar test cases for the Phone Number and Last Name fields as well.
You should see the below output when running the tests:
Understanding Test Lifecycle
Now let's go ahead and understand the Lifecycle of Junit Tests. Each Test undergoes different phases as part of it's execution, each phase is represented by an Annotation.
- @BeforeAll
The method marked with this annotation will be executed before any of the @Test
methods are executed inside the Test class.
- @BeforeEach
The method marked with this annotation will be executed before each @Test
method in the Test class.
- @AfterEach
The method marked with this annotation will be executed after each @Test
method in the Test class.
- @AfterAll
The method marked with this annotation will be executed after all the @Test
methods are executed in the Test class.
The Before methods (@BeforeAll, @BeforeEach) perform some initialization actions like setting up of test data or environment data, before executing the tests.
The After methods (@AfterAll, @AfterEach) perform clean up actions like cleaning up of created Environment Data or Test Data.
@BeforeAll and @AfterAll are called only once for the entire test, and the methods are usually marked as static.
Implementing Lifecycle Annotations in our Test
So now, let’s see how we can use the above mentioned Lifecycle annotations, in our test.
In our 4 tests, we can observe that we are creating an instance of ContactManager at the start of each test.
This logic can go inside the method which is marked with either @BeforeAll or @BeforeEach
@BeforeAll and @AfterAll
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class ContactManagerTest {
private static ContactManager contactManager;
@BeforeAll
public static void setup() {
System.out.println("Instantiating Contact Manager before the Test Execution");
contactManager = new ContactManager();
}
@Test
@DisplayName("Should Create Contact")
public void shouldCreateContact() {
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
@Test
@DisplayName("Should Not Create Contact When First Name is Null")
public void shouldThrowRuntimeExceptionWhenFirstNameIsNull() {
ContactManager contactManager = new ContactManager();
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact(null, "Doe", "0123456789");
});
}
@Test
@DisplayName("Should Not Create Contact When Last Name is Null")
public void shouldThrowRuntimeExceptionWhenLastNameIsNull() {
ContactManager contactManager = new ContactManager();
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact("John", null, "0123456789");
});
}
@Test
@DisplayName("Should Not Create Contact When Phone Number is Null")
public void shouldThrowRuntimeExceptionWhenPhoneNumberIsNull() {
ContactManager contactManager = new ContactManager();
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact("John", "Doe", null);
});
}
}
I created a method called as setup()
which is marked with @BeforeAll
annotation, inside this method, I instantiated the ContactManager and assigned the object to the static
variable.
I also added a method called as tearDown()
which is marked with @AfterAll
annotation, we expect this method to be executed at the end of the test.
If you run the tests again, you should see the output like you see in the below image:
@BeforeEach and @AfterEach
By using this annotation, we can perform some operations which needs to be done Before and After Each Test Execution.
Note that, Junit always creates a new instance of the test before executing each @Test
method.
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class ContactManagerTest {
private ContactManager contactManager;
@BeforeAll
public static void setupAll() {
System.out.println("Should Print Before All Tests");
}
@BeforeEach
public void setup() {
System.out.println("Instantiating Contact Manager");
contactManager = new ContactManager();
}
@Test
@DisplayName("Should Create Contact")
public void shouldCreateContact() {
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
@Test
@DisplayName("Should Not Create Contact When First Name is Null")
public void shouldThrowRuntimeExceptionWhenFirstNameIsNull() {
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact(null, "Doe", "0123456789");
});
}
@Test
@DisplayName("Should Not Create Contact When Last Name is Null")
public void shouldThrowRuntimeExceptionWhenLastNameIsNull() {
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact("John", null, "0123456789");
});
}
@Test
@DisplayName("Should Not Create Contact When Phone Number is Null")
public void shouldThrowRuntimeExceptionWhenPhoneNumberIsNull() {
Assertions.assertThrows(RuntimeException.class, () -> {
contactManager.addContact("John", "Doe", null);
});
}
@AfterEach
public void tearDown() {
System.out.println("Should Execute After Each Test");
}
@AfterAll
public static void tearDownAll() {
System.out.println("Should be executed at the end of the Test");
}
}
I moved the logic to create the ContactManager
object inside the setup()
method marked with @BeforeEach
and I renamed the method marked with @BeforeAll
to setupAll()
I also introduced a new method called as tearDown()
which is just logging some random text, just to show you that the method is executing after each test execution.
If you run the above test, this is how the output should look like:
Default Test Instance Lifecycle
As mentioned above, Junit instantiates the Test class for each method marked with @Test
.
For this reason, the method marked with @BeforeAll
and @AfterAll
should be marked with static
.
Changing Default Test Instance Lifecycle
You can change this default behavior, by instructing Junit to create an instance of the Test class, only once using the below annotation.
@TestInstance(Lifecylce.PER_CLASS)
By using the above annotation, there is no need to mark the @BeforeAll
and @AfterAll
methods as static
.
Conditional Executions
We can execute the @Test
methods in our class based on a specific condition, for example: imagine if you developed a specific functionality on Linux for our Contact Manager application. After saving the contact, we are performing some additional logic which is specific to Linux Operating System.
Then we have to make sure that the test should only run when it’s running only on Linux Operating System.
You can enable this conditional execution using different annotations:
- @EnabledOnOs
- @DisabledOnOs
These annotations take the values for usual Operating Systems like MAC, LINUX, WINDOWS.
You can see the example usage of this annotation below:
@Test
@DisplayName("Should Create Contact")
@EnabledOnOs(value = OS.MAC, disabledReason = "Should Run only on MAC")
public void shouldCreateContact() {
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
We can also provide a reason using the disabledReason field, you can see the output in the below image:
Assumptions
We can also perform conditional Assertions using Junit 5 Assumptions.
There may be particular test cases you want to make sure that you are running based on certain conditions.
You can have some test cases which should be executed only on Developer Machine but not on CI environment.
Or you can also have some slow running tests, you can enable on disable them based on certain conditions.
You can perform this assumptions in Junit 5 similar to the Assertions.
@Test
@DisplayName("Test Contact Creation on Developer Machine")
public void shouldTestContactCreationOnDEV() {
Assumptions.assumeTrue("DEV".equals(System.getProperty("ENV")));
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
- In the above test, you can see that we are using the Assumptions.assumeTrue() method to execute the tests, only when the ENV System Property is equal to DEV.
- If the above assumption failed, then the test will not be executed.
Here is the output when the assumptions is failed:
Now let’s go ahead and add the System Property ENV to the VM Options of the ContactManagerTest
class, you can do that by clicking on the Edit Configuration options for the Test inside IntelliJ.
Once you run the test after activating this property, you can see that the test is executed successfully.
Repeated Tests
If you have any functionality which has some randomness to it, you may want to create and run that test multiple times, to make sure that the functionality is working as expected.
Junit 5 provides us the @RepeatedTest
annotation to fulfil this requirement.
You can repeat the Test N number of time by passing the number as an argument to the annotation.Eg: @RepeatedTest(5)
will run the test marked with this annotation 5 times.
@DisplayName("Repeat Contact Creation Test 5 Times")
@RepeatedTest(5)
public void shouldTestContactCreationRepeatedly() {
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
If you run the above test, you can see the below output:
You can create that IntelliJ displays an expandable node where you can see the output for each repetition.
You can also specify custom names instead of the default repetition 1 of 5, using the name field.
Eg:
@RepeatedTest(value = 5,
name = "Repeating Contact Creation Test {currentRepetition} of {totalRepetitions}")
@DisplayName("Repeat Contact Creation Test 5 Times")
@RepeatedTest(value = 5,
name = "Repeating Contact Creation Test {currentRepetition} of {totalRepetitions}")
public void shouldTestContactCreationRepeatedly() {
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
When you run the above test, you can see the below output:
Parameterized Tests
You can also run parametrized tests by running a test multiple times and providing different set of inputs for each repetition.
We have to add the @ParameterizedTest
annotation to mark a test method as Parameterized Test.
@Test
@DisplayName("Phone Number should start with 0")
public void shouldTestPhoneNumberFormat() {
contactManager.addContact("John", "Doe", "0123456789");
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
In the above testcase, we are checking whether the provided phone number is in the required format or not. i.e.. If the Phone Number is starting with 0
We can run this test against different set’s of input and make sure that the test is working as expected or not.
You can provide the input to the test using different ways:
ValueSource
You can provide input to the Parameterized Test using the @ValueSource
annotation where you can provide a set of string
, long
, double
, float
literals to our test.
Eg: @ValueSource(strings = {“string1″,”string2″,”string3”})
Then you can access this string inside the test by first adding a String parameter to our test.
@DisplayName("Phone Number should match the required Format")
@ParameterizedTest
@ValueSource(strings = {"0123456789", "1234567890", "+0123456789"})
public void shouldTestPhoneNumberFormat(String phoneNumber) {
contactManager.addContact("John", "Doe", phoneNumber);
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
In the above example we are providing the Phone Number String in different formats.
If you run the above test, it provides the below output
The Test verifies whether the Phone Number starts with 0 or not, as we provided invalid inputs for the 2nd and 3rd cases, the tests failed.
MethodSource
We can also use @MethodSource
annotation to provide the input to our Parameterized Tests, using this annotation we will refer the method name which returns the values required for our tests as output.
@DisplayName("Method Source Case - Phone Number should match the required Format")
@ParameterizedTest
@MethodSource("phoneNumberList")
public void shouldTestPhoneNumberFormatUsingMethodSource(String phoneNumber) {
contactManager.addContact("John", "Doe", phoneNumber);
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
private List<String> phoneNumberList() {
return Arrays.asList("0123456789", "1234567890", "+0123456789");
}
In the above test, we declared a method called as phoneNumberList()
which returns the required input values as a List<String>
.
We passed the name of this method as the value to the @MethodSource
annotation.
You can see the below output when you run the test.
CsvSource
You can create an inline CSV which contains the input for the test and reference that using the @CsvSource
annotation.
Ex:
@CsvSource({"1,0123456789", "2,1234567890","3,+0123456789"})
@DisplayName("CSV Source Case - Phone Number should match the required Format")
@ParameterizedTest
@CsvSource({"0123456789", "1234567890","+0123456789"})
public void shouldTestPhoneNumberFormatUsingCSVSource(String phoneNumber) {
contactManager.addContact("John", "Doe", phoneNumber);
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
CsvFileSource
You can also reference a CSV File by using the @CsvFileSource
annotation.
Eg:
@CsvFileSource(resources = "/data.csv")
@DisplayName("CSV File Source Case - Phone Number should match the required Format")
@ParameterizedTest
@CsvFileSource(resources = "/data.csv")
public void shouldTestPhoneNumberFormatUsingCSVFileSource(String phoneNumber) {
contactManager.addContact("John", "Doe", phoneNumber);
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
The contents of the csv looks like below:
0123456789
1234567890
+0123456789
Here is how the expected output looks like:
Disabled Tests
You can disable some tests from running by adding the @Disabled annotation.
@Test
@DisplayName("Test Should Be Disabled")
@Disabled
public void shouldBeDisabled() {
throw new RuntimeException("Test Should Not be executed");
}
If you run the above test, you can see the output from Junit 5 that the test shouldBeDisabled()
is @Disabled
Nested Tests
By now, you may have observed that our ContactManagerTest
contains lots of test methods, we can organize these tests into Nested Tests using the @Nested
annotation.
We are going to take a group of Tests and organize them into a seperate category of tests by creating a class and adding the @Nested
annotation to it.
@Nested
class ParameterizedTests {
@DisplayName("Phone Number should match the required Format")
@ParameterizedTest
@ValueSource(strings = {"0123456789", "1234567890", "+0123456789"})
public void shouldTestPhoneNumberFormatUsingValueSource(String phoneNumber) {
contactManager.addContact("John", "Doe", phoneNumber);
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
@DisplayName("CSV Source Case - Phone Number should match the required Format")
@ParameterizedTest
@CsvSource({"0123456789", "1234567890", "+0123456789"})
public void shouldTestPhoneNumberFormatUsingCSVSource(String phoneNumber) {
contactManager.addContact("John", "Doe", phoneNumber);
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
@DisplayName("CSV File Source Case - Phone Number should match the required Format")
@ParameterizedTest
@CsvFileSource(resources = "/data.csv")
public void shouldTestPhoneNumberFormatUsingCSVFileSource(String phoneNumber) {
contactManager.addContact("John", "Doe", phoneNumber);
assertFalse(contactManager.getAllContacts().isEmpty());
assertEquals(1, contactManager.getAllContacts().size());
}
}
In the above example, you can see that we grouped all the Parameterized Tests together into a separate class.
If I run the tests you will see that the parameterized tests are displayed under a different node.
Running Tests using Maven Surefire Plugin
Usually, you will run the tests when building the application, on a CI server. Maven is a build tool which helps us in automating the manual tasks associated with building the project.
We can use the Maven Surefire Plugin to execute the tests as part of the build, all you have to do is configure the plugin in the pom.xml file.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
You should also add the Maven Compiler Plugin to compile our test classes as part of the build.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!--Use this only when using Java 9+-->
<release>15</release>
<!--
Uncomment this and comment out <release> when using Java 8
<source>1.8</source>
<target>1.8</target>
-->
</configuration>
</plugin>
The configuration section compiles the test classes according to the target JDK version.
If you are using Java 9+ you should just use the release tag and if you are using Java 8, then you should uncomment the source and target section.
After adding these plugins, you can run the tests using the command mvn clean test
λ mvn clean test
[INFO] Scanning for projects…
[INFO]
[INFO] --------------------< org.example:contact-manager >---------------------
[INFO] Building contact-manager 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ contact-manager ---
[INFO] Deleting F:\contact-manager\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ contact-manager ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ contact-manager ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 2 source files to F:\contact-manager\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ contact-manager ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ contact-manager ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to F:\contact-manager\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ contact-manager ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running ContactManagerTest
Should Print Before All Tests
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Instantiating Contact Manager
Should Execute After Each Test
Should be executed at the end of the Test
[WARNING] Tests run: 24, Failures: 0, Errors: 0, Skipped: 3, Time elapsed: 0.215 s - in ContactManagerTest
[INFO]
[INFO] Results:
[INFO]
[WARNING] Tests run: 24, Failures: 0, Errors: 0, Skipped: 3
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.813 s
[INFO] Finished at: 2020-12-26T00:41:57+01:00
[INFO] ------------------------------------------------------------------------
Conclusion
So we reached to the end of the Junit 5 Complete Tutorial, I hope you learned something about Junit 5.
Please don’t forget to share this article with your friends and colleagues if they find it useful.
I will see you in the next article, until then Happy Coding Techies 🙂
Top comments (1)
Post is very good and informative. I spent considerable time and practiced on its examples. Thanks. But I found some mistakes in coding examples snippets. I am listing it in following.
1) Implementing Lifecycle Annotations in our Test
@BeforeAll and @AfterAll
In this section you are creating instance of ContactManager in 3 test methods which are shouldThrowRuntimeExceptionWhenFirstNameIsNull(), shouldThrowRuntimeExceptionWhenLastNameIsNull and shouldThrowRuntimeExceptionWhenPhoneNumberIsNull. This is not required because You already created instance
in setup method marked with @BeforeAll annotation
2) Parameterized Tests
You gave example of Paramterized test for method source using annotation @MethodSource. Here you using instannce method which name phoneNumberList. I think it should be static. I tried with instance but would fail untill I did not make it static.
Again Thanks for such great tutorial.