Guidelines for unit testing

Have you ever wondered what it takes to build a commercial Jet? It often blows my mind to think of the hours engineers spend assembling components together to build the plane. Interestingly enough there are similarities between building software and assembling planes. The individual units for each part of the software application or plane must be thoroughly tested to ensure the overall functionality of the app or plane. The testing of these units is what has become known as unit testing.

Unit testing requires you to test the functionality of individual units/parts/sections of your application in isolation. Testing in isolation ensures that you can confidently pinpoint bugs in code and verify that they have been fixed.

Phases of a Test – Arrange, Act, Assert

There are 3 generally accepted phases for any unit test.

The Arrange phase, is where you create an instance of the class you need to test and also setup up the initial state of any objects. The Act phase, requires you to call the functionality that represents the behavior being tested. Lastly, the Assert phase is where you check what actually happened was expected.

Tips for writing good unit tests?

The primary objective of unit tests is to prove correctness and you can do that by following these simple guidelines.

Prove a contract has been implemented

This is the most basic form of unit testing which verifies that the contract between the caller and method is being adhered to. For example this test validates a driver’s license number . Verifying that a method implements a contract is one of the weakest unit tests one can write.

[Test]
public void ShouldBeValidWhen8DigitsAndStartsWithLetter()
{
var sut = new DriversLicenseValidator();
const string driversLicenseNumber = "A5522123";
Assert.That(sut.IsValid(driversLicenseNumber), Is.True);
}

Verify Computation Results

A stronger unit test involves verifying that the computation is correct. It is useful to categorize your methods into one of the two forms of computation:

  • Data Reduction: occurs when a test accepts multiple inputs and reduces to one resulting output. For example, the verify division test accepts 2 parameters and returns a single output.
  • [Test] public void VerifyDivisionTest()
    {
    Assert.IsTrue(Divide(6, 2) == 3, "6/2 should equal 3!"); 
    }
  • Data Transformation : These tests operate on sets of values

Establish a method correctly handles an external exception

When your code connects to an external service, it is important to determine that your code will handle exceptions gracefully. Attempting to get an external service to throw a specific error is tricky and so the use of Mocking tools will help in this process.

Prove a Bug is Re-creatable

Tests should be repeatable in any environment. They should be able to run in production, QA or even on the bus.

Write positive and negative tests :

Negative tests prove that something is repeatedly not working.They are important in understanding the problem and the solution .Positive tests prove that the problem has been fixed. They are important not only to verify the solution, but also for repeating the test whenever a change is made. Unit testing plays an important role when it comes to regression testing.

[TestMethod] 
[ExpectedException(typeof(ArgumentOutOfRangeException))] 
public void BadParameterTest() 
{ 
    Divide(5, 0);
}

Verify Tests are independent:

Tests should not depend on each other. On test should not set up the conditions for the next test.

These simple guidelines will set you off on the journey of unit testing. Feel free to share any ideas you might have stumbled on.