VI. No unit tests
Driving without headlights
In modern day it’s almost unacceptable to deliver code in any form or shape without testing it
. A concept called Test-driven development (TDD) has been born to accommodate the inclusion of tests into requirements and effectively baking these into the process of producing testable code
.
Let’s first look at this code example below, a simple method returns some sort of calculation result - in this instance calculating postage cost.
1
2
3
4
5
6
7
public class ShoppingCart
{
public double CalculatePostageCost(int zone, double distance)
{
return zone * distance;
}
}
Problem Statement
There’s nothing extraordinary about the piece of code above, it simply multiplies two numbers and returns the result back. However, think about the requirement for a moment, which might look something like this:
- Postage cost returned must be a positive number of double data type
- Zone must be integer within 1 - 7 range
- Distance must be positive number of double data type
In modern day the requirement above would be translated into a unit test
for which structured and testable code would be produced to ensure it meets the requirement.
Solution
Let’s now consider our revised code
which has the requirements
implemented, as per above.
1
2
3
4
5
6
7
8
9
10
11
12
public class ShoppingCart
{
public double CalculatePostageCost(int zone, double distance)
{
if (zone < 1 || zone > 7)
throw new ArgumentOutOfRangeException("Argument zone has to be between 1 - 7");
if (distance <= 0)
throw new ArgumentOutOfRangeException("Argument distance has to be more than 0");
return zone * distance;
}
}
Finally, to complete our example we’ve now written three unit tests
inside ShoppingCartUnitTests test class to make sure the code only does what’s supposed to do.
Test Class
1
2
3
4
5
[TestClass]
public class ShoppingCartUnitTests
{
// Unit Tests go here
}
Unit Test 1 - Numbers are calculated correctly
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[TestMethod]
public void CalculatePostageCost_Returns_CorrectCalculation()
{
// Arrange
var shoppingCart = new ShoppingCart();
int zone = 2;
double distance = 45.77;
// Act
var postageCost = shoppingCart.CalculatePostageCost(zone, distance);
// Assert
Assert.AreEqual(zone * distance, postageCost);
}
Unit Test 2 - Argument zone can only be between 1 - 7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[TestMethod]
public void CalculatePostageCost_Zone_0_Exception()
{
// Arrange
var shoppingCart = new ShoppingCart();
int zone = 0;
double distance = 45.77;
string exception = "Argument zone has to be between 1 - 7";
// Act
try
{
var postageCost = shoppingCart.CalculatePostageCost(zone, distance);
}
catch (ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, exception);
}
}
[TestMethod]
public void CalculatePostageCost_Zone_8_Exception()
{
// Arrange
var shoppingCart = new ShoppingCart();
int zone = 8;
double distance = 45.77;
string exception = "Argument zone has to be between 1 - 7";
// Act
try
{
var postageCost = shoppingCart.CalculatePostageCost(zone, distance);
}
catch (ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, exception);
}
}
Unit Test 3 - Argument distance cannot be 0 or less
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[TestMethod]
public void CalculatePostageCost_Distance_0_Exception()
{
// Arrange
var shoppingCart = new ShoppingCart();
int zone = 6;
double distance = 0;
string exception = "Argument distance has to be more than 0";
// Act
try
{
var postageCost = shoppingCart.CalculatePostageCost(zone, distance);
}
catch (ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, exception);
}
}
Summary
And now when you make future code changes to the CalculatePostageCost method and its behaviour diverge from the unit tests then your code won't build
.
It’s like with the headlights, you
keep the headlights on
while driving in the dark, otherwise you won’t really know where the next turn will take you…