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:
This is more descriptive, but reading the names is still a bit akward:
[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