This is part one in a series that will cover various tools, and the theory behind
them, that can be used to automate the testing process and hence improve software
quality. These posts will not only focus on developer tools but also approach the
problem from other disciplines.
Some of the tools that will be covered are Unit tests (MSUnit), mocking (MoQ), interface
testing via code (WaitN), fitnesse testing via code and English (FitNesse), user
interface testing via English (WebFixture). Along with whatever I come up with in
between.
Now with the introductions done I will dive into the topic of the first part namely
Unit testing. A unit test is a small piece of code that tests whether the logic
of a more complex piece of code is functioning correctly. Some of the do’s and don’ts
of unit tests are:
Do:
- Run Fast
- Test fragile or complicated code (use Code metrics to determine complexity)
- Test difficult to spot errors (rounding etc)
Don't:
- Test private methods
- Hit the database
- Hit external interfaces
- Handle exceptions (we want these thrown)
- Test application performance
But enough rambling let’s take a look at a concrete example:
The sample application we will be building and testing throughout this series will
be a simple webpage that calculates the tax cost on an item based on where you live.
Example Part 1
To start this off we will write a simple function to calculate the tax given the
tax rate and the items cost. So first off we need a solution with an ASP.Net web
application and a business layer class library.
We will need to add a class Calculations to the business layer and create our CalculatePrice function with a simple
return -1 statement.
public class Calculations
{
public static decimal CalculatePrice(decimal price, decimal tax)
{
return -1;
}
}
Ok so now we have our method let’s write a test for it (which we expect to fail
at this point). Right click on the method and select “Create Unit Tests...” which
will bring up a selection of tests the default options are fine so we click ok and
our test project is generated with a default test great!
/// <summary>
///A test for CalculatePrice
///</summary>
[TestMethod()]
public void CalculatePriceTest()
{
Decimal price = new Decimal(); // TODO: Initialize to an appropriate value
Decimal tax = new Decimal(); // TODO: Initialize to an appropriate value
Decimal expected = new Decimal(); // TODO: Initialize to an appropriate value
Decimal actual;
actual = Calculations.CalculatePrice(price, tax);
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
}
Ok so this does not actually test our logic properly so let’s modify it a bit based
on our expectations and see how things go. Remember our do's and don’ts? One of the
do's states “Test difficult to spot errors (rounding etc)” so let’s go ahead and
do this:
/// <summary>
///A test for CalculatePrice
///</summary>
[TestMethod()]
public void CalculatePriceTest()
{
Decimal price = 100.999M;
Decimal tax = 13.993M;
Decimal expected = 115.13M;
Decimal actual;
actual = Calculations.CalculatePrice(price, tax);
Assert.AreEqual(expected, actual);
}
Now we have a test for our function let's run it (right click run tests) and see
what happens.
So it fails but that is exactly what we were expecting seeing as we are just returning
-1. Now let’s actually implement the method properly and try that again.
public static decimal CalculatePrice(decimal price, decimal tax)
{
return decimal.Round(price * ((tax / 100) + 1), 2);
}
Running the test now it passes yay!
So in terms of testing principles we have just gone from red to green, if it were
needed we could now refractor without worrying about breaking our function as we
can simply test if the refractor broke anything.
Now that the Business Layer is implemented we can quickly add the frontend by modifying
our default.aspx page as follows:
Default.aspx:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="costTextBox" runat="server"></asp:TextBox>
<asp:Button ID="calculateButton" runat="server" Text="Calculate"
onclick="calculateButton_Click" />
</div>
<div>
Total Calculated Value:<asp:Label ID="PriceLabel" runat="server" Text=""></asp:Label>
</div>
</form>
</body>
</html>
Default.aspx.cs:
public partial class _Default : System.Web.UI.Page
{
protected void calculateButton_Click(object sender, EventArgs e)
{
decimal cost = 0;
if (decimal.TryParse(costTextBox.Text, out cost))
{
PriceLabel.Text = Calculations.CalculatePrice(cost, 14).ToString();
}
}
}
Browsing to this page we can now calculate the total cost of an item for the hardcoded
tax rate.
Download the example code TestingContinuumPart1.zip (29.46 kb)
So now that we are all up to speed on writing simple unit tests and their uses we
can look forward to part 2 where I will add a dropdown that is populated from the
AdventureWorks database using LinQ to select the tax rate. This will demonstrate
how to unit test without hitting the database and some other general principals.
Hope you enjoyed this article please come back to see the next post in the series
later.