The Automated Testing Continuum - Part1 (Unit Testing)

by nixusg 5. August 2008 19:29

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.

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Unit Testing

Comments

Comments are closed

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen