Unit testing best practices – Avoid relying on test order


Unit testing best practices – Avoid relying on test order

This post is a continuation from our Unit testing best practices series. You can find the previous post here

One of the common traps that a beginner can fall into when learning how to write unit tests is to structure them in a sequence. I know it pretty much because I myself have fallen into it. It can make sense, right? I mean, if you are testing if some data can be created, read and deleted, why not use data from a past unit test? Well, let’s see why you should not be aiming to guarantee a certain test order in your tests.

The cons of relying on unit test order

You have to maintain state, and that is really bad

The goal of an unit test is to assert that a specific part of your code does its job correctly. And in order for us to verify this, this specific part must be isolated from everything else. That means that if we are relying on some other test to prepare something for us, we are not following this principle.

Also, it’s really hard to keep track of what’s going into the unit test if we can’t see it in a glance. Imagine yourself joining a new team and having to discover where the data for a test is being generated. Complicated, right?

A single new test might break all tests

If our unit tests follow some kind of flow, we might find ourselves scratching our heads when trying to insert a new test in our class. If at some point in our tests we need our data to be in a specific way, we might be unable to manipulate our data the way we should, or maybe even having to modify unit tests which semantically have nothing to do with our new test. And then removing a unit test which is not needed anymore? Frightening.

Unable to use test cases

If you’re not familiar with it, test cases, in short, is a functionality which enables you to run the same test multiple times with different parameters. So, if you need your test to be prepared by another test, this feature will probably not be compatible with your tests.

Longer debugging times

In case the unit test brakes, we would not be able to immediately run it again, since we would need to run all other tests before it, so it then can be debugged. This is gruesome. If the problem is hard to find, it can take many runs until you can track it down. And guess what: maybe the problem is not even in this test, it might be due to some other test which did its part wrong.

How to get away from relying on unit test order

Assuming that you are familiar with the Arrange Act Assert pattern (AAA), all the data that’s needed for the test should be prepared during the Arrange phase. Some test suites offer functionalities such as NUnit’s SetUp, but it’s recommended to user helper methods over setups, since it helps to avoid state and improves the readability. I’ve seen cases where unit tests would inherit from another, so we would have multiple setup methods, and that definitely increases the complexity of the code.

If you are persisting data, let me tell you something: you might not have to. Consider using a mocking framework such as Moq along with some abstraction, so then instead of verifying if your data has been written on the disk, for example, you could just verify if the Write() method of your mocked class has been called. You can find a small snippet here which demonstrates how to use Moq.

But, if you can’t avoid persisting data, you should purge it by the end of the unit test. You know the boy scouts rule, right? “Always leave the campground cleaner than you found it.”. Well, in this case you don’t want to leave it cleaner, but exactly as you found it. Feel free to use functionalities such as the TearDown to help you.

Conclusion

Hopefully by now you can agree with me that needing a specific unit test order is a code smell. There are definitely better ways to ensure we have our data in a desired state, as well to purge that data before the next tests kicks in. And, by doing so, we are doing ourselves and our coworkers a favour and boosting our productivity.