Code Behaviour via Unit Tests
Posted
by Dewald Galjaard
on Geeks with Blogs
See other posts from Geeks with Blogs
or by Dewald Galjaard
Published on Tue, 22 Feb 2011 19:33:50 GMT
Indexed on
2011/02/22
23:26 UTC
Read the original article
Hit count: 322
Some four months ago my car started acting up. Symptoms included a sputtering as my car’s computer switched between gears intermittently. Imagine building up speed, then when you reach 80km/h the car magically and mysteriously decide to switch back to third or even second gear. Clearly it was confused!
I managed to track down a technician, an expert in his field to help me out. As he fitted his handheld computer to some hidden port under the dash, he started to explain “These cars are quite intelligent, you know. When they sense something is wrong they run in a restrictive program which probably account for how you managed to drive here in the first place...” I was surprised and thought this was certainly going to be an interesting test drive.
The car ran smoothly down the first couple of stretches as the technician ran through routine checks. Then he said “Ok, all looking good. We need to start testing aspects of the gearbox. Inside the gearbox there are a couple of sensors. One of them is a speed sensor which talks to the computer, which in turn will decide which gear to switch to. The restrictive program avoid these sensors altogether and allow the computer to obtain its input from other [non-affected] sources”. Then, as soon as he forced the speed sensor to come back online the symptoms and ill behaviour re-emerged...
What an incredible analogy for getting into a discussion on unit testing software? Besides I should probably put my ill fortune to some good use, right? This example provide a lot of insight into how and why we should conduct unit tests when writing code. More importantly, it captures what is easily and unfortunately often the most overlooked goal of writing unit tests by those new to the art and those who oppose it alike -
The goal of writing unit tests is to test the behaviour of our code under predefined conditions.
Although it is very possible to test the intrinsic workings of each and every component in your code, writing several tests for each method in practise will soon prove to be an exhausting and ultimately fruitless exercise given the certain and ever changing nature of business requirements. Consequently it is true and quite possible whilst conducting proper unit tests, to call any single method several times as you examine and contemplate different scenarios.
Let’s write some code to demonstrate what I mean. In my example I make use of the Moq framework and NUnit to create my tests. Truly you can use whatever you’re comfortable with.
First we’ll create an ISpeedSensor interface. This is to represent the speed sensor located in the gearbox. Then we’ll create a Gearbox class which we’ll pass to a constructor when we instantiate an object of type Computer. All three are described below.
ISpeedSensor.cs
namespace AutomaticVehicle
{
public interface ISpeedSensor
{
int ReportCurrentSpeed();
}
}
Gearbox.cs
namespace AutomaticVehicle
{
public class Gearbox
{
private ISpeedSensor _speedSensor;
public Gearbox( ISpeedSensor gearboxSpeedSensor )
{
_speedSensor = gearboxSpeedSensor;
}
/// <summary> {
/// This method obtain it's reading from the speed sensor.
/// </summary>
/// <returns></returns>
public int ReportCurrentSpeed()
{
return _speedSensor.ReportCurrentSpeed();
}
}
}
Computer.cs
namespace AutomaticVehicle
public class Computer
{
private Gearbox _gearbox;
public Computer( Gearbox gearbox )
{
}
public int GetCurrentSpeed()
{
return _gearbox.ReportCurrentSpeed( );
}
}
}
Since this post is about Unit testing, that is exactly what we’ll create next. Create a second project in your solution. I called mine AutomaticVehicleTests and I immediately referenced the respective nunit, moq and AutomaticVehicle dll’s. We’re going to write a test to examine what happens inside the Computer class.
ComputerTests.cs
namespace AutomaticVehicleTests
{
[TestFixture]
public class ComputerTests
{
[Test]
public void Computer_Gearbox_SpeedSensor_DoesThrow()
{
// Mock ISpeedSensor in gearbox
Mock< ISpeedSensor > speedSensor = new Mock< ISpeedSensor >( );
speedSensor.Setup( n => n.ReportCurrentSpeed() ).Throws<Exception>();
Gearbox gearbox = new Gearbox( speedSensor.Object );
// Create Computer instance to test it's behaviour towards an exception in gearbox
Computer carComputer = new Computer( gearbox );
// For simplicity let’s assume for now the car only travels at 60 km/h.
Assert.AreEqual( 60, carComputer.GetCurrentSpeed( ) );
}
}
}
What is happening in this test? We have created a mocked object using the ISpeedsensor interface which we've passed to our Gearbox object. Notice that I created the mocked object using an interface, not the implementation. I’ll talk more about this in future posts but in short I do this to accentuate the fact that I'm not not really concerned with how SpeedSensor work internally at this particular point in time.
Next I’ve gone ahead and created a scenario where I’ve declared the speed sensor in Gearbox to be faulty by forcing it to throw an exception should we ask Gearbox to report on its current speed. Sneaky, sneaky.
This test is a simulation of how things may behave in the real world. Inevitability things break, whether it’s caused by mechanical failure, some logical error on your part or a fellow developer which didn’t consult the documentation (or the lack thereof ) - whether you’re calling a speed sensor, making a call to a database, calling a web service or just trying to write a file to disk. It’s a scenario I’ve created and this test is about how the code within the Computer instance will behave towards any such error as I’ve depicted.
Now, if you’ve followed closely in my final assert method you would have noticed I did something quite unexpected. I might be getting ahead of myself now but I’m testing to see if the value returned is equal to what I expect it to be under perfect conditions – I’m not testing to see if an error has been thrown! Why is that? Well, in short this is TDD. Test Driven Development is about first writing your test to define the result we want, then to go back and change the implementation within your class to obtain the desired output (I need to make sure I can drive back to the repair shop. Remember? )
So let’s go ahead and run our test as is. It’s fails miserably... Good!
Let’s go back to our Computer class and make a small change to the GetCurrentSpeed method.
Computer.cs
public int GetCurrentSpeed()
{
try
{
return _gearbox.ReportCurrentSpeed( );
}
catch
{
RunRestrictiveProgram( );
}
}
This is a simple solution, I know, but it does provide a way to allow for different behaviour. You’re more than welcome to provide an implementation for RunRestrictiveProgram should you feel the need to. It's not within the scope of this post or related to the point I'm trying to make. What is important is to notice how the focus has shifted in our approach from how things can break - to how things behave when broken.
Happy coding!
© Geeks with Blogs or respective owner