Friday, February 28, 2014

Two readability tips - naming and organizing tests

I have mentioned earlier that readability is one of the pillars of good unit tests. Making readable unit tests is not trivial. It takes practice, but there are some good practices that can be applied to get a good start.

As an example, let's consider a test which verifies that a vector dot product is calculated correctly.

Naming

How do you name your unit tests? You should be able to get an idea of what a test verifies by just reading the name. Test names like this tell nothing about the tests:

[Test]
public void VectorDotProductTest1()
{
   ...
}

[Test]
public void VectorDotProductTest2()
{
   ...
}

This is more descriptive, but reading the names is still a bit akward:

[Test]
public void TestThatTheVectorDotProductIsZeroWhenOneOfTheVectorsIsZero()
{
   ...
}

[Test]
public void TestThatTheVectorDotProductIsDoneCorrectlyWhenVectorsAreNonZero()
{
   ...
}

Having a standard naming pattern like this makes it easier:

ItemUnderTest_Scenario_ExpectedBehaviour

As you can see, the name is divided into three parts, divided by underscores. The three parts are
  • Item under test: The item, usually a property, method or constructor, which is being tested
  • Scenario: The scenario, e.g. input data, parameters or other prerequisites
  • Expected behaviour: The expected result or outcome of the test

The previous examples would then be:

[Test]
public void DotProduct_OneVectorIsZero_ReturnsZero()
{
   ...
}

[Test]
public void DotProduct_VectorsAreNonZero_ReturnsCorrectProduct()
{
   ...
}



As you can see, dividing the names into sections following a defined pattern makes the names much easier to read.

Organizing the tests

How do you organize your tests? It should be perfectly clear to the user which part of the test is doing setup and preparation (arrangement), which part does the action which is being tested (act) and which part is doing the assertion (assert).

A common and recommended way to achieve this is to follow the "Arrange,Act,Assert" (AAA) pattern. The test is strictly divided into Arrange, Act and Assert sections like this:

[Test]
public void DotProduct_VectorsAreNonZero_ReturnsCorrectProduct()
{
  // Arrange
  var lhsVector = new Vector(1, 2, 3);
  var rhsVector = new Vector(4, 5, 6);

  // Act
  double result = lhsVector.DotProduct(rhsVector);

  // Assert
  Assert.AreEqual(26, result);
}


In this test, the AAA pattern makes it perfectly clear and obvious to the reader which part is doing what. Moreover, this pattern makes it easier to maintain the test. It's much harder to maintain a test where the asserts are spread all over the place.

If you find that it's tempting to add asserts in between the Arrange and Act sections, it's a sign that it's worth considering refactoring the test and/or the production code... or just take deep breath and have a coffee.


No comments:

Post a Comment