Have mocked method output different out param each call with MOQ
Asked Answered
A

2

6

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.

Asci answered 10/2, 2016 at 17:32 Comment(0)
A
3

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.

Asci answered 10/2, 2016 at 17:32 Comment(0)
B
0

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();
Berners answered 16/2, 2016 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.