BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Testing
.NET 1.1+

Using Fakes

The thirteenth part of the Automated Unit Testing tutorial continues the discussion of test doubles that can be used to isolate code under test from its dependencies. This article describes fake objects, which have more functionality than stubs.

Creating the Fake

Now that we have the class to test, we need to implement the fake object. The FakeTransactionReader class recreates the system for reading transactions from the mainframe system, returning different results according to the date range provided. To eliminate the need for a connection to a mainframe to be present, and to increase the speed so that it is acceptable for unit testing, we create a set of results in an ArrayList. Each result represents a single transaction in the expected fixed-width format.

When the GetTransactionsByDate method is called, the start and end parameter values determine the results that are returned. The method loops through the ArrayList, finding all of the transactions that lie within the date range. Each match is appended to a StringBuilder until the loop concludes. At this time, the StringBuilder is converted to a string and returned. Using this process we can create several tests, each returning different results from the fake object.

public class FakeTransactionReader : ITransactionReader
{
    ArrayList _transactions;

    public FakeTransactionReader()
    {
        _transactions = new ArrayList();
        _transactions.Add("20110503ATM WITHDRAWAL (LONDON W1)      -0000015000");
        _transactions.Add("20110504DEPOSIT CHEQUE                  +0000120055");
        _transactions.Add("20110506ATM WITHDRAWAL (MANCHESTER)     -0000010000");
        _transactions.Add("20110506ATM WITHDRAWAL (LEEDS)          -0000010000");
        _transactions.Add("20110507CASH DEPOSIT                    +0000006500");
    }

    public string GetTransactionsByDate(DateTime start, DateTime end)
    {
        DateTime trxDate;
        StringBuilder sb = new StringBuilder();
        foreach (string trx in _transactions)
        {
            trxDate = DateTime.ParseExact(
                trx.Substring(0, 8), "yyyyMMdd", CultureInfo.InvariantCulture);
            if (start <= trxDate && trxDate <= end)
                sb.Append(trx);
        }
        return sb.ToString();
    }
}

Using the Fake in Tests

Now that we have the fake class we can create some unit tests for the TransactionProcessor. The first test fixture processes the three transactions from the fake object that are dated between 4 May 2011 and 6 May 2011. The test fixture is shown below. The important point to note is the use of the fake object.

[TestFixture]
public class WhenProcessingAValidSetOfTransactions
{
    AccountTransaction[] _tranactions;

    [SetUp]
    public void SetUp()
    {
        DateTime start = new DateTime(2011, 05, 04);
        DateTime end = new DateTime(2011, 05, 06);
        ITransactionReader fakeReader = new FakeTransactionReader();
        TransactionProcessor processor = new TransactionProcessor(fakeReader);
        _tranactions = processor.GetTransactionsByDate(start, end);
    }

    [Test]
    public void TheCorrectNumberOfTransactionsIsPresent()
    {
        Assert.AreEqual(3, _tranactions.Length);
    }

    [Test]
    public void TheFirstTransactionIsCorrect()
    {
        AccountTransaction trx = _tranactions[0];
        Assert.AreEqual("20110504", trx.TransactionDate.ToString("yyyyMMdd"));
        Assert.AreEqual("DEPOSIT CHEQUE", trx.Description);
        Assert.AreEqual(1200.55, trx.Amount);
    }

    [Test]
    public void TheSecondTransactionIsCorrect()
    {
        AccountTransaction trx = _tranactions[1];
        Assert.AreEqual("20110506", trx.TransactionDate.ToString("yyyyMMdd"));
        Assert.AreEqual("ATM WITHDRAWAL (MANCHESTER)", trx.Description);
        Assert.AreEqual(-100, trx.Amount);
    }

    [Test]
    public void TheThirdTransactionIsCorrect()
    {
        AccountTransaction trx = _tranactions[2];
        Assert.AreEqual("20110506", trx.TransactionDate.ToString("yyyyMMdd"));
        Assert.AreEqual("ATM WITHDRAWAL (LEEDS)", trx.Description);
        Assert.AreEqual(-100, trx.Amount);
    }
}

Unlike with a stub that returns a preset value, the fake object actually includes a simple implementation of the transaction reader. We can therefore create a second test fixture that reads a different date range. In the tests below, the date range is outside of the set of transactions in the ArrayList so we can test the result of reading an empty set of transactions.

[TestFixture]
public class WhenProcessingAnEmptySetOfTransactions
{
    AccountTransaction[] _tranactions;

    [SetUp]
    public void SetUp()
    {
        DateTime start = new DateTime(2011, 01, 01);
        DateTime end = new DateTime(2011, 01, 01);
        ITransactionReader fakeReader = new FakeTransactionReader();
        TransactionProcessor processor = new TransactionProcessor(fakeReader);
        _tranactions = processor.GetTransactionsByDate(start, end);
    }

    [Test]
    public void TheCorrectNumberOfTransactionsIsPresent()
    {
        Assert.AreEqual(0, _tranactions.Length);
    }
}
4 May 2011