Test Naming Convention
Tests are an important part of any project and should be written to the same standards of your production code. In this post we will cover how you can use a test naming convention to help keep your test names clear and easy to understand.
We will be approaching this from the Apple world of XCTest
, but the same approach can be used for any development project where tests are being utilised.
Why should I care?
Being able to easily understand what is being tested and under which circumstances will help you in many ways. A few examples are:
- Less likley to miss scenarios that need testing
- Quick feedback when tests fail
- Easier to maintain tests as functionality evolves
- Easier to find and remove redundant tests
- Consistency in test naming
What makes a good test name?
A good test name should provide the following information:
- What is being tested
- Under which circumstances is is being tested
- What is the expected result
Having all this information provided in the test names prevents you and other project members from having to read through the test code to determine what is being tested.
How to write good test names?
As established above, a good test name consists of three elements. Using these elements, we can come up with a convention that can be followed when naming a unit test. For example, we could use the following convention:
By simply using the three elements of information mentioned above, we can come up with an easy to use naming convention.
func testWhatIsBeingTestedUnderWhichCircumstancesWhatIsTheExpectedResult {}
The elements can be simplified down to:
func testMethodNameCircumstancesExpectation {}
We can further add clarity to the test name with the addition of the keywords with
and should
:
func testMethodNameWithCircumstancesShouldExpectation {}
One last step we can take is to introduce underscores (_
) to the test names, in combination with camel case. Whilst it may seem strange to use underscores, it makes them much easier to read and understand at a glance. So now we end up with our final naming convention:
func test_methodName_withCircumstances_shouldExpectation {}
Real world examples
Now that we've described a naming convention, lets see how it can be applied to some real life tests. Below are a couple of real test names taken from the Albert Heijn iOS codebase, before they implemented the test naming convention.
func testStartStoreBonusCardShouldTriggerStateChangedToBusyForAuthenticatedMember {}
func testEndStoreBonusCardShouldTriggerStateChangedToIdleForAuthenticatedMember {}
With these test names it is very difficult to get an understanding of what is being tested, under which circumstances it is being tested and what the expected result is, especially with a quick glance.
It is quite easy in this situation for a developer to waste time looking for a method named startStoreBonusCard
and/or endStoreBonusCard
. They will come up empty handed though as these methods do not exist in the codebase.
The method that is actually being tested here is storeBonusCard
. Having start
and/or end
prefixed to the method names really adds no value and only leads to developer confusion on what is actually being tested. The developer is forced to dig into the test and method code before they can begin to understand what is being tested and why.
By adopting the test naming convention, we can vastly improve the readability of the unit test names and instantly get understanding of what is being tested, under which circumstances and what the expected result is.
Here is the result after applying the test naming convention to these tests:
func test_storeBonusCard_withAuthenticatedMember_shouldSetStateToBusy {}
func test_storeBonusCard_withAuthenticatedMemberAndSuccessResponse_shouldSetStateToIdle {}
And there we have it, these unit tests names are now much easier to read and understand. This will help speed up your development time greatly and help keep sanity within the development team.
Should I rename all my tests?
Now that you've decided to adopt a test naming convention, you're probably dreading the task of applying this to all your existing tests. There is no need to worry though.
The best approach to take is to apply the naming convention to all new tests that are written. Existing tests can be updated when an change to the test is required. For example, when the functionality has been updated. Overtime, all your tests will come to use this naming convention.
What else can be done to improve tests?
Applying a test naming convention is just a starting point. In fact, the convention discussed here is by no means perfect. It can create long tests names which become exceptionly long when you have many variables making up the circumstances under which the test is ran.
If you're intersted in keeping your test names short, you can read this post by Victor Magalhães. Here he discusses a method that allows you to describe tests in the test code itself. Whilst this is not exactly our favourite method, it is definitely worth the read. Maybe it can meet your needs.
As well as readable test names, you need to have readable test code. It should be written with a good structure too. There are also other approaches you can take to writing tests, that can help you to write more concise tests. Make sure to keep checking back here if you're interested in reading more about good practices for writing tests.
For now, happy testing!