Getting Started with JUnit 5: Part 3 (AssertJ)

Aziz Kale
Javarevisited
Published in
5 min readOct 2, 2023

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));
}
  1. 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 the citiesInGerman list. If the value is present, it returns false, indicating that the condition is not satisfied; otherwise, it returns true.
  2. @Test public void testNoCitiesInList() { ... }: This is the test method. Inside it, I created cityList2, which contains cities not present in the citiesInGerman list.
  3. assertThat(citiesInGerman).have(noCitiesInList(cityList2));: I use AssertJ's fluent assertion to check if the citiesInGerman list meets the custom condition defined by noCitiesInList(cityList2). Specifically, it checks if none of the cities in cityList2 are present in citiesInGerman. 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 Employeeat 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
}
  1. 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 for Employee 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 using super(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 myFileon 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");

}
}
  1. exists(): Checks if the file exists at the specified path.
  2. hasFileName("myFile.txt"): Verifies that the file has the expected file name.
  3. canWrite(): Ensures that the file is writable.
  4. hasExtension("txt"): Checks if the file has the expected file extension.
  5. hasName("myFile.txt"): Verifies that the file has the expected name.
  6. contentOf(file): Reads the content of the file.
  7. 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.

--

--

Aziz Kale
Javarevisited

Highly Motivated, Passionate Full Stack Developer | EMM-IT Co. | Web: azizkale.com