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.

What are Fake Objects?

Previously I described the use of stubs during unit testing. Stubs allow a class or method being tested to be isolated from its real dependencies, instead substituting an object that returns known results. Fake objects, or fakes, are another type of object that can take the place of a dependency and guarantee fast, repeatable results.

The term "fake" can be controversial, as different groups of developers define it in different ways. Some programmers use fake object as a generic term for any object that takes the place of a dependency. With this definition, stubs and mocks, which we have yet to see in this tutorial, are types of fake object. Other developers use the term test double for this purpose and define stubs, fakes and mocks as three types of test double. In this tutorial I am using the following definitions:

  • Dummy. A test double with no functionality that is used simply to fill a required parameter during a call.
  • Stub. A simple test double that provides canned answers when reading properties or calling methods.
  • Fake Object. A more complicated test double that includes an implementation of the real dependency that is sufficiently complex to execute tests but not suitable for deployment into a live environment. Fakes remove a problem that is specific to the testing scenario, such as improving performance, eliminating a connectivity issue or removing complexity.
  • Mock Object. Usually provided by a third-party isolation framework, mock objects are test doubles that can be pre-programmed to return specified results and to expect calls to be made. The expectations can be verified after the Act stage of the Arrange, Act, Assert (AAA) pattern. Mocks will be considered in the next article in this series.

Fake Example

To demonstrate fake objects we'll create a simple example that could be used to eliminate a connectivity issue and slow performance that would be unacceptable during test runs. In the example we will be testing a class that obtains a list of banking transactions in a long string containing fixed-width fields, and converts the string into an array of AccountTransaction objects. The class retrieves the string from a dependency that talks to a mainframe system. As the mainframe may not always be available, may have connectivity problems and may be slow to return results, this dependency will be faked.

The fake will return a string containing transactions in the same format as the real mainframe access class. Each transaction contains an eight-digit date, a description of up to 32 characters that is padded with spaces where necessary and an eleven digit value including a + or - prefix. The last two digits of the value are assumed to be after the omitted decimal point.

The following shows two sample transactions in a string:

20110503ATM WITHDRAWAL (LONDON W1)      -000001500020110504DEPOSIT CHEQUE                  +0000120055

The class that we are testing converts the above string to two transactions with the following property values.

Transaction DateDescriptionAmount
3 May 2011ATM WITHDRAWAL (LONDON W1)-150.00
4 May 2011DEPOSIT CHEQUE1,200.55

ITransactionReader Interface

Let's start the code with the interface for the class that we will be faking. To keep the examples simple, the interface includes just one method, which retrieves the string containing the transactions. The method has two parameters that allow the date range for the transactions to be specified.

public interface ITransactionReader
{
    string GetTransactionsByDate(DateTime start, DateTime end);
}

TransactionProcessor Class

The class that we want to test is named TransactionProcessor. The code is shown below. As you can see, the dependency is injected via the constructor. The class includes one public method that reads the transactions and breaks the string into 51-character chunks. Each of these is converted into a transaction object using the two private methods. Finally, the AccountTransaction class is shown with automatically implemented properties. If you are using an earlier version of the .NET framework, expand these to full properties with backing store variables.

public class TransactionProcessor
{
    ITransactionReader _reader;

    public TransactionProcessor(ITransactionReader reader)
    {
        _reader = reader;
    }

    public AccountTransaction[] GetTransactionsByDate(DateTime start, DateTime end)
    {
        string raw = _reader.GetTransactionsByDate(start, end);
        int count = raw.Length / 51;
        AccountTransaction[] transactions = new AccountTransaction[count];
        for (int i = 0; i < count; i++)
            transactions[i] = ExtractTransaction(raw, i);
        return transactions;
    }

    private AccountTransaction ExtractTransaction(string rawData, int transactionNumber)
    {
        string transactionData = rawData.Substring(transactionNumber * 51, 51);
        return BuildTransaction(transactionData);
    }

    private AccountTransaction BuildTransaction(string transactionData)
    {
        string date = transactionData.Substring(0, 8);
        DateTime trxDate = DateTime.ParseExact(
            date, "yyyyMMdd", CultureInfo.InvariantCulture);
        AccountTransaction transaction = new AccountTransaction();
        transaction.TransactionDate = trxDate;
        transaction.Description = transactionData.Substring(8, 32).Trim();
        transaction.Amount = decimal.Parse(transactionData.Substring(40, 11)) / 100;
        return transaction;
    }
}

public class AccountTransaction
{
    public DateTime TransactionDate { get; set; }
    public string Description { get; set; }
    public decimal Amount { get; set; }
}
4 May 2011