DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Querydsl vs. JPA Criteria, Part 4: Pagination
  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
  • Introduction: Querydsl vs. JPA Criteria
  • What Java DAO Layer Is Best for Your Project

Trending

  • A Guide to Developing Large Language Models Part 1: Pretraining
  • Transforming AI-Driven Data Analytics with DeepSeek: A New Era of Intelligent Insights
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • It’s Not About Control — It’s About Collaboration Between Architecture and Security
  1. DZone
  2. Data Engineering
  3. Databases
  4. Demystifying Sorting Assertions With AssertJ

Demystifying Sorting Assertions With AssertJ

Know the options for sorting assertions in AssertJ, including ascending/descending order, multiple attributes, null handling, and case-insensitive sorting.

By 
Arnošt Havelka user avatar
Arnošt Havelka
DZone Core CORE ·
Feb. 05, 25 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
7.0K Views

Join the DZone community and get the full member experience.

Join For Free

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:

Spring Data JPA solution for the PDM model

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:

Plain Text
 
[
    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 isSorted method (line 5)
Java
 
@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 cityRepository with the findAll method (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 name attribute from the City entity (line 11). This is necessary as our City entity doesn't implement the Comparable interface. More details are covered at the end of this article.
    • Assert the correct order with isSorted (line 12) -> this is our goal.
Java
 
import static org.springframework.data.domain.Sort.Direction.ASC;

@Test
void sortingByNameAscending() {
	var pageable = PageRequest.of(0, 5, ASC, City_.NAME);
	
	Page<City> page = cityRepository.findAll(pageable);

	assertThat(page.getContent())
		.hasSize(5)
		.map(City::getName)
		.isSorted();
}


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.

Java
 
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.

Java
 
import static java.util.Collections.reverseOrder;
import static org.springframework.data.domain.Sort.Direction.DESC;

@Test
void sortingByNameDescending() {
	var pageable = PageRequest.of(0, 5, DESC, City_.NAME);
	
	Page<City> page = cityRepository.findAll(pageable);

	assertThat(page.getContent())
		.hasSize(5)
		.map(City::getName)
		.isSortedAccordingTo( reverseOrder() );
}


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 getCountryNameComparator method (lines 13-15).
    • Assert the correct order by the city name (line 10) simply by providing the desired function to the Comparator.thenComparing method.
Java
 
@Test
void sortingByCountryAndCityNames() {
	var countryNameSorting = City_.COUNTRY + "." + Country_.NAME;
	var pageable = PageRequest.of(0, 15, ASC, countryNameSorting, City_.NAME);
	
	Page<City> page = cityRepository.findAll(pageable);

	assertThat(page.getContent())
		.isSortedAccordingTo( getCountryNameComparator()
                .thenComparing( City::getName ));
}

private Comparator<City> 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:

Plain Text
 
[
    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 null value in the beginning (line 6).
  • Assert null value in the beginning with Comparator.nullsFirst comparator and the ascending order by using Comparator.naturalOrder comparator.
Java
 
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.

Java
 
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsFirst;

@Test
void sortingByStateAscending() {
	var pageable = PageRequest.of(0, 15, ASC, City_.STATE);
	
	Page<City> page = cityRepository.findAll(pageable);

	assertThat(page.getContent())
		.map(City::getState)
		.isSortedAccordingTo(nullsFirst(naturalOrder()));
}


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) and 
    • Comparator.nullsLast – to check that nulls are at the end -> as we have descending sorting,
    • Collections.reverseOrder – to check the descending order and
    • String.CASE_INSENSITIVE_ORDER – to compare values ignoring the case sensitivity.
Java
 
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.

Java
 
import static java.util.Collections.reverseOrder;
import static java.util.Comparator.nullsLast;
import static String.CASE_INSENSITIVE_ORDER;

@Test
void sortingByStateDescending() {
	var pageable = PageRequest.of(0, 15, DESC, City_.STATE);
	
	Page<City> page = cityRepository.findAll(pageable);

	assertThat(page.getContent())
		.map(City::getState)
		.isSortedAccordingTo(nullsLast(reverseOrder(CASE_INSENSITIVE_ORDER)));
}


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:

Java
 
import static org.springframework.data.domain.Sort.Direction.ASC;

@Test
void failByNotProvidingCorrectComparator() {
	var pageable = PageRequest.of(0, 5, ASC, City_.NAME);
	
	Page<City> page = cityRepository.findAll(pageable);

	assertThat(page.getContent())
		.hasSize(5)
		// .map(City::getName)
		.isSorted();
}


We get the some elements are not mutually comparable in group error when the map function is commented out (line 11).

Plain Text
 
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.

Sorting Spring Data Assertion (software development) Framework

Opinions expressed by DZone contributors are their own.

Related

  • Querydsl vs. JPA Criteria, Part 4: Pagination
  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
  • Introduction: Querydsl vs. JPA Criteria
  • What Java DAO Layer Is Best for Your Project

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!