Quantcast
Channel: CodeProject Latest postings for Dominic Burford
Viewing all articles
Browse latest Browse all 280

Getting the Most Out of Your Unit Tests

$
0
0
Whilst recently viewing the code coverage results from one of our applications, I was looking for areas which contained poor code coverage to see if there was any way to improve code coverage in those areas. One area that can be difficult to unit test are exception conditions. If you are implementing structured exception handling using
try / catch
blocks, then it can be challenging to unit test the code contained within the catch block. Although most (if not all) unit testing frameworks contain mechanisms for testing exceptions, it can be difficult to set up the conditions that will trigger an exception.

I tend to follow the rule of thumb that states "If you aren't going to handle an exception then don't catch it". There is little point catching an exception if all your code does is throw the exception back up the call stack. In the case of my ASP.NET WebAPI application, all the controllers contain structured exception handling. This is the last chance saloon to catch any exceptions before handing control back to the client, so it makes sense to catch exceptions on the part of the application exposed to the client. I also log all exceptions so that I can later diagnose them.

I also catch exceptions in my data layer. I implement a retry mechansim on those methods that do not use the Azure Service Bus (a service bus architecture will automatically implement a retry mechanism if an exception is thrown and place the request back on the service bus queue where it can be re-tried again). These are the only specific areas of the application where I have implemented structured exception handling.

When implementing the business layer, I wanted to ensure I could unit test the various methods without the data layer methods having to actually connect to the data itself. So I implemented an architecture that allowed this from the ground up. My business layer classes contain a reference to an interface that implements the data handling methods. My unit tests implement this interface with implementations of the various methods under test. This is then injected into the constructor of the business layer class at run time by the unit tests. My business layer class contains a default constructor which instantiates the default SQL Server data layer class. It also contains a constructor which accepts an instance of the interface containing the definitions of methods that have been implemented specifically for unit testing. So with good design from the very outset it is perfectly possible to unit test your entire business layer using constructor injection.

This is the definition of the SQL Server data class. Notice it implements the IMyInterface interface.
publicclass MyDataClass: BaseData, IMyInterface
{
	// implement data methods here
}
This is the definition of the unit test data class. Notice it also implements the IMyInterface interface.
publicclass MyUnitTestDataClass: IMyInterface
{
	// implement data methods here
}
Here is the definition of the IMyInterface interface.
publicinterface IMyInterface
{
    List<Mileage> GetPreviousMileages(int driverId);
    List<Driver> GetDriverVehicles(int driverId);
}
The business layer class then implements constructor injection so that it can accept either a concrete instance of the unit test implementation or the SQL Server implementation.
publicclass MyBusinessService
{
    privatereadonly IMyInterface _data;
 
    ///<summary>/// If no data class is passed to the constructor then use the default data class.
///</summary>public MyBusinessService()
    {
        this._data = new MyDataClass();
    }
 
    ///<summary>/// Allow unit testing fixtures to implement their own version of the data class
/// which can be injected via the constructor.
///</summary>///<paramname="data"></param>public MyBusinessService(IMyInterface data)
    {
        this._data = data;
    }
}
The unit tests instantiate the business layer by injecting a unit test specific instance of the interface, as in the following example.
[TestMethod]publicvoid MyTestMethods()
{
    //Act
    MyBusinessService service = new MyBusinessService(new MyUnitTestDataClass()); //inject your unit test class here//Arrangevar result = service.GetPreviousMileages(123);
 
    //Assert
    Assert.IsNotNull(result);
    Assert.IsTrue(result.Any());
}
I will delve more into unit testing in future articles as it's an area where huge benefits can be made to increasing the quality of the software. It also forces you to write code in such a way that it is unit testable in the first place, and this in itself is a good reason to implement unit testing within a development team. If you're not writing unit tests, you're doing it wrong.
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." - C.A.R. Hoare

Home | LinkedIn | Google+ | Twitter

Viewing all articles
Browse latest Browse all 280


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>