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 3.5+

Using Mocks

The fourteenth part of the Automated Unit Testing tutorial concludes the description of isolating a class from its dependencies using test doubles. This article explains how mock objects can be used to fake large classes and to test expected behaviour.

Matching Rules

Another way to match multiple values in an expectation is with the It.Is method. This is applied in a similar way to It.IsAny but adds an argument that describes the rule that determines if a value matches. The rule is usually defined with a predicate in the form of a lambda expression. When the test is running and the mock object's method is called, the predicate is evaluated for the argument used. If the result is true, the action associated with the expectation is executed.

The following sample test calculates the totals for periods 1, 2 and 3. The first of the expectations uses It.Is with a predicate of "i => i == 1 || i == 2". This will return an array containing the values 1, 2, 3 and 4 when called with a period number of 1 or 2. The third period is handled by the second expectation.

[Test]
public void TotalIsCorrectForMultipleMatchingPeriodsWithTransactions2()
{
    var mockRetriever = new Mock<ITransactionRetriever>();
    mockRetriever.Setup(m => m.GetTransactions(It.Is<int>(i => i == 1 || i == 2))).Returns(
        new decimal[] { 1, 2, 3, 4 });
    mockRetriever.Setup(m => m.GetTransactions(3)).Returns(new decimal[] { 5, 6, 7 });
    var calculator = new BalanceCalculator(mockRetriever.Object);
    Assert.AreEqual(43, calculator.GetBalance(5, 1, 3));
}

Verifying Calls

So far we have seen how mock objects can be programmed to behave like stubs. Mock objects are far more powerful than this and are especially useful for testing the behaviour of the code under test, rather than just its final state. One of the key functions is the ability to verify that calls have been made, even if no expectation has been set. You can use this to ensure that calls have been made to dependencies when no events are raised, exceptions thrown or values returned. This would otherwise require you to create fake objects that record calls made.

Let's demonstrate with another example. Here we are testing that the transaction retriever is used when we calculate the balance for a period. We are testing the interaction with the dependency but not the result of the operation. The mock object is created and injected into the balance calculator with no expectations set and the call to GetBalance is made. The Assert part of the Arrange, Act, Assert (AAA) pattern is the call to the Verify method with a lambda expression to describe the call that should have been made. If the call has been made the test passes. If not, it fails.

[Test]
public void TotallingAPeriodsMeansARetrieve()
{
    var mockRetriever = new Mock<ITransactionRetriever>();
    var calculator = new BalanceCalculator(mockRetriever.Object);
    calculator.GetBalance(0, 1);
    mockRetriever.Verify(m => m.GetTransactions(1));
}

As with setting expectations, the It class can be used to widen the range of argument values that will cause successful verification. The test below passes if the transaction retriever is used to obtain any period's transactions.

[Test]
public void TotallingAPeriodsMeansARetrieve2()
{
    var mockRetriever = new Mock<ITransactionRetriever>();
    var calculator = new BalanceCalculator(mockRetriever.Object);
    calculator.GetBalance(0, 1);
    mockRetriever.Verify(m => m.GetTransactions(It.IsAny<int>()));
}

Verifying the Number of Calls

Often verifying that a call has been made is not enough. Instead you will wish to verify that the same call was made a specific number of times. We can add this restriction with a second parameter in the Verify method and the Times structure. Times has several methods that indicate how often the member has been used with a particular set of arguments. There are various options including Once, Never and Exactly. In this article we'll use Exactly only.

The following test ensures that when a balance is calculated for three periods, the GetTransactions method is called exactly three times. This is a useful test as it ensures that we are not making more calls than we need to, unnecessarily accessing the transactions database.

[Test]
public void TotallingThreePeriodsMeansThreeRetrieves()
{
    var mockRetriever = new Mock<ITransactionRetriever>();
    var calculator = new BalanceCalculator(mockRetriever.Object);
    calculator.GetBalance(0, 1, 3);
    mockRetriever.Verify(m => m.GetTransactions(It.IsAny<int>()), Times.Exactly(3));
}

Verifying All Expectations

The final mock objects technique that we will examine here is to verify all expectations. If you set up a number of expectations against a mock object and you also wish to verify that all of the calls were made, you could create a matching set of Verify calls. However, this creates additional code and can be tedious. To simplify the process you can make a call to the mock object's VerifyAll method during the Assert stage of the AAA pattern. If every expectation defined with the Setup method was used, the method runs normally. If any expectation was not met, an exception is thrown and the test fails.

To demonstrate, let's look at an example that initially does not make a call to VerifyAll. The test code below sets up expectations and return values for periods 1, 2, 3 and 4. However, the calculator is used to total periods 1, 2 and 3 only, so the final expectation is never used. The test passes though, as the Assert phase only tests the returned value.

[Test]
public void TotalIsCorrectForMultiplePeriodsWithTransactions2()
{
    var mockRetriever = new Mock<ITransactionRetriever>();
    mockRetriever.Setup(m => m.GetTransactions(1)).Returns(new decimal[] { 1, 2, 3, 4 });
    mockRetriever.Setup(m => m.GetTransactions(2)).Returns(new decimal[] { 5, 6, 7 });
    mockRetriever.Setup(m => m.GetTransactions(3)).Returns(new decimal[] { 8, 9, 10 });
    mockRetriever.Setup(m => m.GetTransactions(4)).Returns(new decimal[] { 11, 12 });
    var calculator = new BalanceCalculator(mockRetriever.Object);
    Assert.AreEqual(60, calculator.GetBalance(5, 1, 3));
}

Let's modify the test by adding a call to the mock's VerifyAll method, shown below.

[Test]
public void TotalIsCorrectForMultiplePeriodsWithTransactions3()
{
    var mockRetriever = new Mock<ITransactionRetriever>();
    mockRetriever.Setup(m => m.GetTransactions(1)).Returns(new decimal[] { 1, 2, 3, 4 });
    mockRetriever.Setup(m => m.GetTransactions(2)).Returns(new decimal[] { 5, 6, 7 });
    mockRetriever.Setup(m => m.GetTransactions(3)).Returns(new decimal[] { 8, 9, 10 });
    mockRetriever.Setup(m => m.GetTransactions(4)).Returns(new decimal[] { 11, 12 });
    var calculator = new BalanceCalculator(mockRetriever.Object);
    Assert.AreEqual(60, calculator.GetBalance(5, 1, 3));
    mockRetriever.VerifyAll();
}

Now when you run the test you will see that it fails with the message:

Example_Tests.MoqTests.TotalIsCorrectForMultiplePeriodsWithTransactions3:
    Moq.MockVerificationException : The following setups were not matched:
    ITransactionRetriever m => m.GetTransactions(4)

The VerifyAll call detects that the GetTransactions method is never called for period four so throws a MockVerificationException. In this test this would indicate that the fourth expectation should be removed as it is unnecessary to the process.

15 May 2011