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+

Generate Return Values Using Lambdas in Moq

Mock objects and stubs created using the Moq framework are generally used to inject dependencies with expectations that define fixed results. For more complex scenarios, lambda expressions can be used to generate results based on provided arguments.

Moq Expectations

In previous articles I've described how mocks and stubs created using the Moq isolation framework can be configured with expectations. These expectations define the result of using a member of the mocked interface or class and can include returning a value or throwing a predefined exception. The examples have included expectations that return a fixed value. However, in some cases your unit tests will require more flexibility from their mocked dependencies. For example, you may wish that the value returned from a parameterless method or property varies over time, or you may want to use the values passed to a member's parameters in a calculation that generates a return value. You can achieve this by using a lambda expression in the Returns section of an expectation.

Creating a Test Interface and Mock Object

Let's start with a simple example using an expectation for a method that does not accept arguments. Firstly, we need an interface to mock. We will use the ITestObject interface shown below. This includes two methods; one has no parameters and one accepts two integer arguments. Both return 64-bit integer results.

public interface ITestObject
{
    long GetResult();
    long CalculateResult(int x, int y);
}

We can now create a mock ITestObject using the following code:

Mock<ITestObject> mockTestObject = new Mock<ITestObject>();

Creating an Expectation that Returns the Result of a Lambda Expression

With the mock object in place we can try setting some expectations and checking that the results are as we expect. For the purposes of this article I will not show unit tests; we will output the results of calling the mocked interface's methods to the console. Although this is not a typical use, it demonstrates the syntax without requiring an additional test framework such as NUnit.

We'll begin with a typical expectation for the GetResult method. The code below initialises the expectation, returning the current date and time as a number of ticks.

mockTestObject.Setup(m => m.GetResult()).Returns(DateTime.Now.Ticks);

The above code is ideal if you wish to fix the number of ticks returned. The DateTime.Now.Ticks property is evaluated when creating the expectation so the mock object will always return the same value. We can see this by executing the method twice and outputting the results:

ITestObject test = mockTestObject.Object;
Console.WriteLine(test.GetResult());    // 634601518560535048
Console.WriteLine(test.GetResult());    // 634601518560535048

If you want to delay the evaluation of the returned value, you can provide Moq with a lambda expression to run when the expectation is met. The lambda is executed each time the expectation is triggered, giving a different result each time. This is shown in the code below. Note that the value passed to the Returns method has been changed to be a lambda expression and the outputted values now differ.

mockTestObject.Setup(m => m.GetResult()).Returns(() => DateTime.Now.Ticks); 
            
ITestObject test = mockTestObject.Object;
Console.WriteLine(test.GetResult());    // 634601519452316055
Console.WriteLine(test.GetResult());    // 634601519452326056

NB: Using a lambda expression in this manner can be used to queue return values, allowing a series of fixed values to be returned from a single expectation.

Using Setup Parameters in the Lambda Expression

If the method for which you are setting an expectation has parameters, it can be useful to use the values passed to those parameters to generate a return value. This allows you to create very powerful mocks and stubs with minimal effort. To use the argument values, you simply create matching parameters in the lambda expression and use them to calculate a return value as desired.

The second method in the ITestObject interface, "CalculateResult", accepts two integer arguments. If we use a lambda expression that also has two integer parameters Moq will correctly map the values. We can see this in the next example. Here the two integers are multiplied and the product is returned. Note that we are using the It.IsAny syntax to capture any two integer values.

mockTestObject.Setup(m => m.CalculateResult(
    It.IsAny<int>(), It.IsAny<int>())).Returns((int x, int y) => x * y);
            
ITestObject test = mockTestObject.Object;
Console.WriteLine(test.CalculateResult(5, 8));      // 40
Console.WriteLine(test.CalculateResult(10, 20));    // 200

The use of parameters in this way is not restricted to working with It.IsAny. You can include discrete values in the Setup call or use It.Is and a lambda expression to define the values that will trigger the expectation. This is shown in the final example. When the method is called with the first argument having a value less than ten, the result is the sum of the two integers. When the first argument is ten or greater, the product of the values is returned instead.

mockTestObject.Setup(m => m.CalculateResult(
    It.Is<int>(i => i < 10), It.IsAny<int>())).Returns((int x, int y) => x + y);
mockTestObject.Setup(m => m.CalculateResult(It.Is<int>(i => i >= 10),
    It.IsAny<int>())).Returns((int x, int y) => x * y);
            
ITestObject test = mockTestObject.Object;
Console.WriteLine(test.CalculateResult(5, 8));      // 13
Console.WriteLine(test.CalculateResult(10, 20));    // 200
22 December 2011