Getting Started with JUnit 5: Part 3 (AssertJ)
I will continue discussing JUnit 5 in this article, which serves as a follow-up to the previous project. The project covered in this article is the same one mentioned in the previous piece. You can review or download the project via this link, and access the previous article through this link.
In this article, we will explore the powerful Java testing library known as AssertJ. AssertJ is designed to simplify the process of writing clear and expressive test assertions in your Java code.
It provides a rich and intuitive API that allows developers to create highly readable and maintainable tests, making it an invaluable tool for ensuring the reliability and correctness of your software.
Now, let’s explore AssertJ through some illustrative examples.
I am creating the class AssertJTest
at the path:
src/test/java/com/azizkale/junittestingproject/assertj/AssertJTest.java:
package com.azizkale.junittestingproject.assertj;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class AssertJTest {
@Test
public void testString(){
String city1 = "Berlin";
assertThat(city1)
.isEqualTo("Berlin")
.startsWith("Ber")
.endsWith("lin")
.isNotEmpty()
.isNotNull()
.doesNotContain("k")
.containsOnlyOnce("Be");
}
}
As seen above, with AssertJ, you can write multiple tests one after another in a concise and expressive manner.
AssertJ simplifies working with arrays, making it easy to perform tests on them. I am adding another function to the same class:
List<String> citiesInGerman = new ArrayList<>(Arrays.asList("Berlin","Frankfurt","Muenchen"));
@Test
public void testList() throws Exception{
assertThat(citiesInGerman)
.contains("Berlin")
.doesNotContain("New York")
.containsExactly("Berlin","Frankfurt","Muenchen")
.doesNotHaveDuplicates();
}
The function names are self-explanatory, allowing for chaining multiple consecutive functions to create expressive and concise test assertions for string expressions.
We can also define our custom conditions for testing by using the class Condition
. I am adding the code below to the same class:
private Condition<? super String> noCitiesInList(List<String> otherList) {
return new Condition<String>(){
@Override
public boolean matches(String value) {
return citiesInGerman.contains(value);
}
};
}
@Test
public void testNoCitiesInList() {
List<String> cityList2 = Arrays.asList("Paris", "London", "Amsterdam");
assertThat(citiesInGerman).have(noCitiesInList(cityList2));
}
private Condition<? super String> noCitiesInList(List<String> otherList) { ... }
: This method defines a custom condition. It takes another list as a parameter (otherList
) and returns a condition that checks if a given string value is present in thecitiesInGerman
list. If the value is present, it returnsfalse
, indicating that the condition is not satisfied; otherwise, it returnstrue
.@Test public void testNoCitiesInList() { ... }
: This is the test method. Inside it, I createdcityList2
, which contains cities not present in thecitiesInGerman
list.assertThat(citiesInGerman).have(noCitiesInList(cityList2));
: I use AssertJ's fluent assertion to check if thecitiesInGerman
list meets the custom condition defined bynoCitiesInList(cityList2)
. Specifically, it checks if none of the cities incityList2
are present incitiesInGerman
. If this condition is satisfied, the test will pass; otherwise, it will fail.
This test illustrates how you can create custom conditions in AssertJ to perform more complex and specific assertions beyond the built-in methods, making your test cases highly expressive and tailored to your requirements.
Custom Assertions with AssertJ
Another notable feature is the ability to create custom assert classes tailored to your domain objects. By extending the AbstractAssert
class, you can craft specialized assertion methods that provide clear and concise validation of your objects' state.
To create an example for this, I’m creating a class named Employee
at the path:
src/main/java/com/azizkale/junittestingproject/Employee.java
package com.azizkale.junittestingproject;
public class Employee {
private Long id;
private String name;
private List<String> duties;
private String gender;
private Integer age;
//getters
//setters
}
Now I am creating an assert class for this class at the path:
src/test/java/com/azizkale/junittestingproject/customassert/EmployeeAssert.java
package com.azizkale.junittestingproject.customassert;
import com.azizkale.junittestingproject.Employee;
import org.assertj.core.api.AbstractAssert;
public class EmployeeAssert extends AbstractAssert<EmployeeAssert, Employee> {
public EmployeeAssert(Employee actual) {
super(actual, EmployeeAssert.class);
}
public static EmployeeAssert assertEmployee(Employee actual) {
return new EmployeeAssert(actual);
}
// custom assertion methods described later
}
- Class Declaration (
public class EmployeeAssert extends AbstractAssert<EmployeeAssert, Employee>
):
EmployeeAssert
is the name of our custom assertion class.- It extends
AbstractAssert<EmployeeAssert, Employee>
, indicating that it's an AssertJ assertion class forEmployee
objects.
2. Constructor (public EmployeeAssert(Employee actual)
):
- This constructor takes an instance of an
Employee
object (actual
) as its parameter. - It calls the constructor of the superclass
AbstractAssert
usingsuper(actual, EmployeeAssert.class);
. This sets up the assertion context.
3. Static Factory Method (public static EmployeeAssert assertEmployee(Employee actual)
):
- This is a static factory method that provides a more readable and fluent way to create an instance of
EmployeeAssert
. Instead of using the constructor, we can call this method to create an assertion instance.
Now I am creating my custom assertion functions in the class EmployeeAssert
:
public EmployeeAssert hasName(String name) {
isNotNull();
if (!actual.getName().equals(name)) {
failWithMessage("Expected person to have full name %s but was %s", name, actual.getName());
}
return this;
}
public EmployeeAssert isAdult() {
isNotNull();
if (actual.getAge() < 18) {
failWithMessage("Expected person to be adult");
}
return this;
}
public EmployeeAssert hasDuty(String duty) {
isNotNull();
if (!actual.getDuties().contains(duty)) {
failWithMessage("Expected person to have duty %s", duty);
}
return this;
}
Now I am creating the test class and I will call my custom assertion functions. At the path:
src/test/java/com/azizkale/junittestingproject/customassert/EmployeeTest.java
package com.azizkale.junittestingproject.customassert;
import com.azizkale.junittestingproject.Employee;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import static com.azizkale.junittestingproject.customassert.EmployeeAssert.assertEmployee;
public class EmployeeTest {
@Test
public void testEmployee(){
Employee employeeToTest = new Employee();
employeeToTest.setName("Aziz Kale");
employeeToTest.setGender("Male");
employeeToTest.setAge(35);
ArrayList<String> duties = new ArrayList<>(Arrays.asList("Testing", "Development"));
employeeToTest.setDuties(duties);
assertEmployee(employeeToTest)
.hasName(employeeToTest.getName())
.isAdult()
.hasDuty("Testing");
}
}
As seen above, I can use the static factory method assertThatCustom
just like the assertThat
method, and I can call my custom assertion functions one after another.
Testing Files
AssertJ provides powerful features for testing files in Java applications. With AssertJ, you can easily perform various file-related assertions, such as checking file existence, file attributes, content, and more. These capabilities make it a valuable tool for ensuring your file handling code behaves as expected.
Now, let’s examine examples related to this. I created a file named myFile
on the desktop and I am adding the class FileTest
to the path: src/test/java/com/azizkale/junittestingproject/assertj/FileTest.java
import org.junit.jupiter.api.Test;
import java.io.File;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.contentOf;
public class FileTest {
@Test
public void testFile(){
File file = new File("Your file path here!");
assertThat(file)
.exists()
.hasFileName("myFile.txt")
.canWrite()
.hasExtension("txt")
.hasName("myFile.txt");
assertThat(contentOf(file))
.startsWith("Lorem");
}
}
exists()
: Checks if the file exists at the specified path.hasFileName("myFile.txt")
: Verifies that the file has the expected file name.canWrite()
: Ensures that the file is writable.hasExtension("txt")
: Checks if the file has the expected file extension.hasName("myFile.txt")
: Verifies that the file has the expected name.contentOf(file)
: Reads the content of the file.startsWith("Lorem")
: Checks if the content of the file starts with the specified text "Lorem".
Conclusion
In our discussion, we explored the capabilities of AssertJ and how to leverage it for testing our Java code. We learned that we can create custom assertion functions by extending the AbstractAssert
class. When working with files in our projects, AssertJ proves to be a convenient library for testing, enhancing the robustness of our software.
You can access the project I used in this article by this link.
Thank you for your time and for reading.