LWood
LWood

Reputation: 457

Have mocked method output different out param each call with MOQ

Is there a way with MOQ to have sequential calls on a method return different out parameters? For a quick example getting the method:

public void OutputANumber(out int number)

to output 1, followed by 2 (ignore the fact it could return int, it's for an example only not real code).

int number = 1;
mock.Setup(n => n.OutputANumber(out number));
number = 2;
mock.Setup(n => n.OutputANumber(out number));

Doesn't work as the second setup overrides the first, likewise a SetupSequence only allows for sequential returns.

Upvotes: 6

Views: 1283

Answers (2)

JamesR
JamesR

Reputation: 745

LukeW's solution can work out, if you need just two sequential calls. But with longer sequence it can become too complex. You can solve it much easier with Typemock Isolator. Take a look on the example below:

public class ClassUnderTest
{
    public void OutputANumber(out int number)
    {
        number = 3;
    }
}

[TestMethod, Isolated]
public void TestOutSequence()
{
    //Arrange
    var n = new ClassUnderTest();

    int number = 1;
    Isolate.WhenCalled(() => n.OutputANumber(out number)).IgnoreCall();
    number = 2;
    Isolate.WhenCalled(() => n.OutputANumber(out number)).IgnoreCall();
    Isolate.WhenCalled(() => n.OutputANumber(out number)).CallOriginal();

    //Act
    var res1 = 0;
    var res2 = 0;
    var resOriginal = 0;
    var resDefault = 0;
    n.OutputANumber(out res1);
    n.OutputANumber(out res2);
    n.OutputANumber(out resOriginal);
    n.OutputANumber(out resDefault);

    //Assert
    Assert.AreEqual(1, res1);
    Assert.AreEqual(2, res2);
    Assert.AreEqual(3, resOriginal);
    Assert.AreEqual(3, resDefault);
}

No need to create a dummy objects or additional methods. By serial defining out param and using Isolate.WhenCalled() you can set up any sequence of out parameters:

int number = 1;
Isolate.WhenCalled(() => n.OutputANumber(out number)).IgnoreCall();

The last call to Isolate.WhenCalled() defines the default behavior for the method from that moment onwards. So, in the example I returned the original implementation to be called after fake calls:

Isolate.WhenCalled(() => n.OutputANumber(out number)).CallOriginal();

Upvotes: 0

LWood
LWood

Reputation: 457

One solution is to use a callback to pass the mock into a method that runs a new setup like so

int number = 1;
mock.Setup(n => n.OutputANumber(out number)).Callback(() => ChangeOut(mock));

void ChangeOut(Mock<type> mock)
{
    int number = 2;
    mock.Setup(n => n.OutputANumber(out number));
}

or you could create a dummy object that tracks the number of calls and outputs a new value as needed.

Upvotes: 3

Related Questions