First steps to using JUnit 5
In my previous article on Unit Tests and Mockito, I discussed the advantage of using Mockito to manipulate object instances to focus on testing just what you want. That is awesome, you can use many combinations of returns and test all possibilities your method needs.
The latest versions of JUnit and Mockito brings some syntax and behavior changes. In this article, I want to explain how to start using JUnit 5 and hope to answer the following questions:
1. How can I update my project without changing all the tests simultaneously?
2. How can I use the new features?
First of all, I’m checking the release notes. It’s essential to do this because I can look directly into this library's primary source of knowledge, the documentation, what’s new, and what’s deprecated. Here I can find the Release Notes of JUnit 5.9.2
1. How can I update my project without changing all the tests simultaneously?
Let’s say I want to upgrade the JUnit, but I can’t change all tests within my project. I must adjust by taking baby steps because that takes some time and I can’t stop all my features and bug fixes just to adjust the tests.
Then I must add this Jupiter JUnit dependency:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
2. How can I use the new features?
Thinking in baby steps, I’m starting with syntax changes. For example, if I remove the junit-vintage-engine dependency, some errors will happen on my tests.
2.1. RunWith, Before, and Test annotations
Like the RunWith, Before, and Test annotations. Note that the first change is the packages path.
On JUnit 4 it was org.junit.*
, now in JUnit 5 it is org.junit.jupiter.*
.
To execute the test using Mockito, the annotation @RunWith(MockitoJUnitRunner.class)
changes to @ExtendWith(MockitoExtension.class)
.
When I need to execute code before each test, in JUnit 4 I use @Before
, now on JUnit 5 I use @BeforeEach
.
I also need to change the class that makes the assertions of my test. That class will compare the values and return a positive or negative value. Change from Assert.assertEquals
to Assertions.asserEquals
.
It is possible to compare the following two blocks.
JUnit 4
import org.junit.*;
import org.junit.runner.RunWith;
@RunWith(MockitoJUnitRunner.class)
class MyTest {
Customer myCustomer = null;
CustomerRegisterUseCase registerUseCase = null;
@Before
public void setUp() {
myCustomer = new Customer();
registerUseCase = new CustomerRegisterUseCase();
}
@Test
public void should_validade_customer_data() {
...
Assert.assertEquals(true, registerUseCase.register(myCustomer));
}
}
JUnit 5
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MockitoExtension.class)
class MyTest {
Customer myCustomer = null;
CustomerRegisterUseCase registerUseCase = null;
@BeforeEach
public void setUp() {
myCustomer = new Customer();
registerUseCase = new CustomerRegisterUseCase();
}
@Test
public void should_validade_customer_data() {
...
Assertions.assertEquals(true, registerUseCase.register(myCustomer));
}
}
2.2. DisplayName
Ok, moving forward with new features of JUnit 5. Let’s say I want to have a better name for my test method, to identify it better when running the tests using an IDE. For that I may use @DisplayName:
@Test
@DisplayName("Should register a new customer with success")
public void register_success() throws InvalidValueException {
...
}
My test output should be the following:
2.3. ParameterizedTest
Since JUnit version 5.7 we can take advantage of the facilities of the Parameterized Tests. And why not save time and lines of code using this feature?
A parametrized test is a manner to run a test several times with different values.
According to the documentation:
@ParameterizedTest
is used to signal that the annotated method is a parameterized test method.
And it works in conjunction with another annotation:
@ParameterizedTest
methods must specify at least oneArgumentsProvider
via@ArgumentsSource
or a corresponding composed annotation (e.g.,@ValueSource
,@CsvSource
, etc.). The provider is responsible for providing aStream
ofArguments
that will be used to invoke the parameterized test method.
So, basically, we mark in the test method that it will receive parameters to execute, and these parameters will come from other annotations, such as @ValueSource
or @CsvSource
.
To start using the @ParameterizedTest
you should first add the following dependency:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
Considering the class CustomerRegister
, where there is the use case method to register a new Customer, and another method to validate if the Customer CPF is real.
The junit-jupiter-param library brings us some options for using parameterized tests, which are:
ValueSource
CsvSource
CsvFileSource
To use this feature, I should create a csv file to store my tests cases, something like that:
And my test should be something like that:
Conclusion
These are some of the new features you can use, exists more than that, but I hope that would be the first step for you to start using JUnit 5.
Keeping your JUnit up to date will speed up the creation of your tests, and also maintain compatibility with the latest versions of Java.
I hope you’ve learned something, but don’t stop there. Go ahead, create more tests, and increase the quality of your software.