There are times when a new feature containing sorting is introduced. Obviously, we want to verify that the implemented sorting works correctly. AssertJ framework provides first-class support for such tasks. This article shows how to write such tests.
In this article, you will learn the following:
- Two main methods provided by Assert frameworks for sorting assertion
- How to assert data sorted in ascending or descending way
- How to assert data sorted by multiple attributes
- How to deal with
nulls or case-insensitive sorting
Introduction
First of all, we need to know that AssertJ provides the AbstractListAssert abstract class for asserting any List argument. This class also contains the isSorted and isSortedAccordingTo methods for our purpose. The solution used there is based on the Comparator interface and its several static methods. Before moving to our examples, let’s have a short introduction to the data and technology used in this article.
Almost every case presented here is demonstrated with two examples. The simple one is always presented first, and it’s based on a list of String values. The goal is to provide the simplest example which can be easily taken and tested. However, the second example is based on the DB solution introduced in the Introduction: Querydsl vs. JPA Criteria article. There, we use Spring Data JPA solution for the PDM model defined as:

These tables are mapped to City and Country entities. Their implementation is not mentioned here, as it’s available in the article mentioned earlier. Nevertheless, the data used in the examples below are defined like this:
[
City(id=5, name=Atlanta, state=Georgia, country=Country(id=3, name=USA)),
City(id=13, name=Barcelona, state=Catalunya, country=Country(id=7, name=Spain)),
City(id=14, name=Bern, state=null, country=Country(id=8, name=Switzerland)),
City(id=1, name=Brisbane, state=Queensland, country=Country(id=1, name=Australia)),
City(id=6, name=Chicago, state=Illionis, country=Country(id=3, name=USA)),
City(id=15, name=London, state=null, country=Country(id=9, name=United Kingdom)),
City(id=2, name=Melbourne, state=Victoria, country=Country(id=1, name=Australia)),
City(id=7, name=Miami, state=Florida, country=Country(id=3, name=USA)),
City(id=4, name=Montreal, state=Quebec, country=Country(id=2, name=Canada)),
City(id=8, name=New York, state=null, country=Country(id=3, name=USA)),
City(id=12, name=Paris, state=null, country=Country(id=6, name=France)),
City(id=11, name=Prague, state=null, country=Country(id=5, name=Czech Republic)),
City(id=9, name=San Francisco, state=California, country=Country(id=3, name=USA)),
City(id=3, name=Sydney, state=New South Wales, country=Country(id=1, name=Australia)),
City(id=10, name=Tokyo, state=null, country=Country(id=4, name=Japan))
]
Finally, it’s time to move to our examples. Let’s start with a simple isSorted method.
isSorted Method
AssertJ framework provides the isSorted method in order to verify values that implement the Comparable interface, and these values are in a natural order. The simplest usage can be seen in the dummyAscendingSorting test as:
- Define sorted values (line 3)
- Assert the correct order with
isSortedmethod (line 5)
@Test
void dummyAscendingSorting() {
var cities = List.of("Atlanta", "London", "Tokyo");
assertThat(cities).isSorted();
}
Now, let’s imagine a more real-like example where the data is provided by Spring Data JPA. This use case is demonstrated in the sortingByNameAscending test as:
- Define a pagination request for loading data. Here, we request data sorted just by city name and the page with the size of 5 (line 5).
- Load cities from
cityRepositorywith thefindAllmethod (line 5). - Assert the loaded data as:
- Check the number of cities returned by the search (line 10) -> to be equal to the requested page size.
- Extract a
nameattribute from theCityentity (line 11). This is necessary as ourCityentity doesn’t implement theComparableinterface. More details are covered at the end of this article. - Assert the correct order with
isSorted(line 12) -> this is our goal.
Reverse Order
In some cases, we use descending order. The sorting assertion can be handled in a pretty similar way, but we need to use the isSortedAccordingTo method instead of the isSorted method. This method is used for the advanced sorting assertion.
The simplest assertion for values sorted in descending ways can be seen in the dummyDescendingSorting test. This is the same as the usage of the isSorted method, but this time, we need to use the already-mentioned isSortedAccordingTo method with the Collections.reverseOrder comparator.
import static java.util.Collections.reverseOrder;
@Test
void dummyDescendingSorting() {
assertThat(List.of("Tokyo", "London", "Atlanta")).isSortedAccordingTo( reverseOrder() );
}
The real-like solution is demonstrated by the sortingByNameDescending test. It’s very similar to the previous sortingByNameAscending test, but this time, we use data loaded from DB.
Custom Comparator
Sometimes, we use sorting by multiple attributes. Therefore, we cannot use the simple approach shown in previous examples. For asserting sorting by multiple attributes, we need to have a comparator. This case is demonstrated in the sortingByCountryAndCityNames test as:
- Define a pagination request with ascending sorting first by the country name and then by the city name (line 4). Now, we use a higher page size in order to load all available data.
- Assert the loaded data as:
- Assert the correct order by the country name (line 4) with the custom comparator implemented in the
getCountryNameComparatormethod (lines 13-15). - Assert the correct order by the city name (line 10) simply by providing the desired function to the
Comparator.thenComparingmethod.
- Assert the correct order by the country name (line 4) with the custom comparator implemented in the
assertThat(page.getContent())
.isSortedAccordingTo( getCountryNameComparator()
.thenComparing( City::getName ));
}
private Comparator
return ( c1, c2 ) -> c1.getCountry().getName().compareTo(c2.getCountry().getName());
}” data-lang=”text/x-java”>
@Test
void sortingByCountryAndCityNames() {
var countryNameSorting = City_.COUNTRY + "." + Country_.NAME;
var pageable = PageRequest.of(0, 15, ASC, countryNameSorting, City_.NAME);
Page page = cityRepository.findAll(pageable);
assertThat(page.getContent())
.isSortedAccordingTo( getCountryNameComparator()
.thenComparing( City::getName ));
}
private Comparator getCountryNameComparator() {
return ( c1, c2 ) -> c1.getCountry().getName().compareTo(c2.getCountry().getName());
}
In order to promote your understanding, the data by Spring Data JPA loaded in the sortingByCountryAndCityNames test is listed below as:
[
City(id=1, name=Brisbane, state=Queensland, country=Country(id=1, name=Australia)),
City(id=2, name=Melbourne, state=Victoria, country=Country(id=1, name=Australia)),
City(id=3, name=Sydney, state=New South Wales, country=Country(id=1, name=Australia)),
City(id=4, name=Montreal, state=Quebec, country=Country(id=2, name=Canada)),
City(id=11, name=Prague, state=null, country=Country(id=5, name=Czech Republic)),
City(id=12, name=Paris, state=null, country=Country(id=6, name=France)),
City(id=10, name=Tokyo, state=null, country=Country(id=4, name=Japan)),
City(id=13, name=Barcelona, state=Catalunya, country=Country(id=7, name=Spain)),
City(id=14, name=Bern, state=null, country=Country(id=8, name=Switzerland)),
City(id=5, name=Atlanta, state=Georgia, country=Country(id=3, name=USA)),
City(id=6, name=Chicago, state=Illionis, country=Country(id=3, name=USA)),
City(id=7, name=Miami, state=Florida, country=Country(id=3, name=USA)),
City(id=8, name=New York, state=null, country=Country(id=3, name=USA)),
City(id=9, name=San Francisco, state=California, country=Country(id=3, name=USA)),
City(id=15, name=London, state=null, country=Country(id=9, name=United Kingdom))
]
Sorting With NULLs
Some data might contain a null value, and we need to deal with it. This case is covered in the dummyAscendingSortingWithNull test as:
- Define data with
nullvalue in the beginning (line 6). - Assert
nullvalue in the beginning withComparator.nullsFirstcomparator and the ascending order by usingComparator.naturalOrdercomparator.
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsFirst;
@Test
void dummyAscendingSortingWithNull() {
assertThat(Arrays.asList(new String[] { null, "London", "Tokyo" }))
.isSortedAccordingTo(nullsFirst(naturalOrder()));
}
The same approach in our real-like solution is available in the sortingByStateAscending test.
It is also possible to receive a null at the end instead of the beginning. Let’s see this case in our last example.
A Complex Sorting Example
Our last example demonstrates a more complex scenario. Our goal is to verify the order of our data sorted in descending and case-insensitive order. Additionally, this data contains null values. The simple usage is in the dummyDescendingSortingWithNull test as:
- Define sorted values (line 7).
- Assert the correct order with
isSortedAccordingTo(line 8) andComparator.nullsLast– to check that nulls are at the end -> as we have descending sorting,Collections.reverseOrder– to check the descending order andString.CASE_INSENSITIVE_ORDER– to compare values ignoring the case sensitivity.
import static java.util.Collections.reverseOrder;
import static java.util.Comparator.nullsLast;
import static String.CASE_INSENSITIVE_ORDER;
@Test
void dummyDescendingSortingWithNull() {
assertThat(Arrays.asList(new String[] { "London", "atlanta", "Alabama", null}))
.isSortedAccordingTo(nullsLast(reverseOrder(CASE_INSENSITIVE_ORDER)));
}
The same approach in our real-like solution is available in sortingByStateDescending test.
Known Pitfall
When dealing with sorting, it’s easy to forget we can apply sorting functions only to instances implementing the Comparable interface. In our case, the City entity doesn’t implement this interface. The appropriate comparator depends on our sorting. Therefore, the comparator can be different for every sortable attribute or their combinations. Let’s demonstrate this situation from our first example by the failByNotProvidingCorrectComparator test as:
We get the some elements are not mutually comparable in group error when the map function is commented out (line 11).
java.lang.AssertionError:
some elements are not mutually comparable in group:
[City(id=5, name=Atlanta, state=Georgia, country=Country(id=3, name=USA)),
City(id=13, name=Barcelona, state=Catalunya, country=Country(id=7, name=Spain)),
City(id=14, name=Bern, state=null, country=Country(id=8, name=Switzerland)),
City(id=1, name=Brisbane, state=Queensland, country=Country(id=1, name=Australia)),
City(id=6, name=Chicago, state=Illionis, country=Country(id=3, name=USA))]
at com.github.aha.sat.jpa.city.CityRepositoryTests$FindAll.sortingByNameAscending(CityRepositoryTests.java: 71)
at java.base/java.lang.reflect.Method.invoke(Method.java: 580)
at java.base/java.util.ArrayList.forEach(ArrayList.java: 1597)
at java.base/java.util.ArrayList.forEach(ArrayList.java: 1597)
at java.base/java.util.ArrayList.forEach(ArrayList.java: 1597)
}
Such simplification is wrong, but it can happen from time to time when we try to simplify our code.
Summary and Source Code
First, the article explained the basics of sorting with the isSorted method. Next, sorting assertions for data in reverse order and sorting by two criteria using the custom comparator were demonstrated.
After that, the sorting for data with null values was covered. Finally, the pitfall related to the misuse of sorting assertions provided by the AssertJ framework was explained.
The complete source code presented above is available in my GitHub repository.
Opinions expressed by DZone contributors are their own.