logo
Solutionunittest

JUnit 5 ordered tests

Views: 787 Created: 2019-06-22 Read time: 4 minutes
Post Preview
Tags:

1)     Preface

Junit 5 among its vast set of features offers the ability to customise the order in which the tests are run within a test class. At first glance, it is not easy to think about a scenario in which this feature would be useful. Let us take a deeper dive into test ordering to find situations where it could actually be of value.

 

2)     Junit 5 test ordering engine

By default there is no way to determine in which order the tests will be run but Junit 5 makes sure that the order is always the same. The documentation describes it as deterministic but intentionally nonobvious.

There three ways that the library provides and which allow laying our hands on that nonobviousness:

       Alphanumeric: sorts methods alphanumerically on their names using String.compareTo(String).

       OrderAnnotation: sorts methods based on the @Order annotation.

       Random: orders method pseudo-randomly.

 

We will try to focus exclusively on the order annotation option as others are pretty much irrelevant.

 

Note
Note

 

3)     Junit 5 @TestMethodOrder: integration test scenario

In the context of unit testing, method order should never be essential or relevant in terms of the success of the test suite. When we switch the context to integration testing, there is a light in the dark for customised ordering.

When we write IT tests for simple CRUD REST operations, we usually do the following:

       Clean the database entry after a successful POST request.

       Restore the original state of entry after a PUT request.

       Add new entry into the database just before a DELETE request.

       Add new entry before a GET request and remove it after it is successful.

 

Now that is all fine and dandy but what an overkill of operations that needs to be performed around the actual tests. What if we take advantage of the custom ordering that Junit 5 provides us and make our CRUD testing a bit simpler:

 

Test Code


@TestMethodOrder(OrderAnnotation.class)
public class UserCrudIT{

	private MockMvc mockMvc;

	@Test
	@Order(1)
	public void shouldAddUser_givenPostRequest(){
		// When
		mockMvc.performPost(postUserObject)
			.andExpect(isOk());
	}

	@Test
	@Order(2)
	public void shouldFindUser(){
		// When
		User user = mockMvc.performGet(userId);

		// Then
		assertThat(user).isNotNull();
	}

	@Test
	@Order(3)
	public void shouldUpdateUser_givenPutRequest(){
		// Given
		User user = mockMvc.performGet(userId);

		// When
		User updatedUser = mockMvc.performPut(putUserObject);

		// Then
		assertThat(user).isNotEqualTo(updatedUser);
	}

	@Test
	@Order(4)
	public void shouldAddUser_givenPostRequest(){
		// When
		mockMvc.performDelete(userId);

		// Then
		User user = mockMvc.performGet(userId);
		assertThat(user).isNull();
	}

 

Here we can see that methods are meant to be invoked as follows:

Order 1) First, we start off with the POST method that would serve as a supplier of data for the following tests.

Order 2) Then we can follow up with either a GET or a PUT. It does not really matter, but if the PUT is run first, we have to adjust our GET test accordingly to the change.

Order 3) GET or PUT. Depends on point 2.

Order 4) Finally, once we have verified that POST, PUT and GET work as expected, we can check that DELETE operation also does what it is supposed to do. It also acts as a natural clean-up method for the entire set.

 

As we see, thanks to the custom order, we were able to structure our CRUD tests in a way that they are complementing each other and also make the test class a lot simpler and readable.

 

Note
Note

 

4)     Junit 5 MethodOrdered: create your custom test order

If we find a pattern in our IT tests in regards to when each type of test should be invoked within a test class, then Junit 5 allows us to create a custom ordering engine.

Let's take a look at an example test class:

 

Test Code


@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;

@Test
public void shouldProcessUserCreditScore(){
	// Given
	User user = userRepository.findUser(1L);

    // When
	Short newCreditScore = userService.processCreditScore(user);
	...
}

@Test
public void shouldDeactivateUser_whenLastLoginLongAgo(){
	// Given
	User user = userRepository.findUser(1L);

    // When
	boolean deadctivated = userService.deactiveIfNecessary(user);
	...
}

@Test
public void shouldSaveUser(){

@Test
public void shouldUpdateUser(){

@Test
public void shouldFindUser(){

@Test
public void shouldDeleteUser(){

By studying this case, we have found that the order of execution is quite important and when appropriately implemented can save us a lot of time.

All of the service testing methods here relly on a data access layer that is exclusively tested by a group of specialised methods.

The service tests take in general more time to process. Let us imagine that the Junit engine decides to run the service methods first followed by the data access methods. All the service methods run successfully but the last one. This method updates the user, and the data access method responsible for this is corrupted. Only if we run the update tests firsts, we would not have to waste so much time on running all those extensive service tests.

Data access layer tests are a bit faster, and if any of them fails, naturally, the service tests are also irrelevant at this point until the data access layer itself is fixed.

What we want then is all the data access layer tests to run before the service layer tests:

 


public class DataAccessFirstOrdering implements MethodOrderer {

	public static final String PATTERN = "(.*find.*)|(.*delete.*)|(.*update.*)|(.*save.*)";


	@Override
    public void orderMethods(MethodOrdererContext context) {
        private final Pattern pattern = Pattern.compile(PATTERN)

		context.getMethodDescriptors().sort(
			(MethodDescriptor methodOne, MethodDescriptor methodTwo) ->{
				Matcher matcherOne = methodOne.getMethod().getName().toLowerCase();
				Matcher matcherTwo = methodTwo.getMethod().getName().toLowerCase();

				if(matcherOne.find() && !matcherTwo.find()){
					return 1;
				}

				if(!matcherOne.find() && matcherTwo.find()){
					return -1;
				}

				return 0;
			})
		);
    }
}

 

Test Code


@TestMethodOrder(DataAccessFirstOrdering.class)
public class UserIT{

 

Here we define a custom MethodOrderer implementation, which would examine the names of our test methods. If a method contains any of the keywords: find, delete, update, save, it would naturally take precedence.

 

5)     Conclusion

 

Summary

 

In general, we will not need to interfere in the ordering process of our tests. If there is a need though, we have a customisable pallet of options thanks to which our tests have no other choice than to get in line as we tell them.

 

Need more insight?
Repository
Repository
Glossary
Glossary
Tags:
Reference
You may also like:
junit5-parameterized-tests
JUnit 5 parameterized tests
junit5-conditional-execution
JUnit 5 conditional execution
Comments
Be the first to comment.
Leave a comment