4

I try to use the SetupSequence method of the Moq 4.5 framework.

Class that should be mocked:

public class OutputManager {
    public virtual string WriteMessage(string message) {
    // write a message
    }
}

Mock:

var outputManagerMock = new Mock<OutputManager>();
var writeMessageCalls = 0;
var currentMessage = String.Empty;

outputManagerMock.Setup(o => o.WriteMessage(It.IsAny<string>())).Callback((string m) => {
    writeMessageCalls++;
    message = m;
});

This code works fine. But I'd like having a different setup for each call of the WriteMessage method. Well, I use SetupSequence instead of Setup:

var outputManagerMock = new Mock<OutputManager>();
var writeMessageCalls = 0;
var firstMessage = String.Empty;
var secondMessage = String.Empty;

outputManagerMock.SetupSequence(o => o.WriteMessage(It.IsAny<string>()))
.Callback((string m) => {
    writeMessageCalls++;
    firstMessage = m;
}).Callback((string m) => {
    writeMessageCalls++;
    secondMessage = m;
});

Then I've got the error:

Error CS0411 The type arguments for method
'SequenceExtensions.SetupSequence<TMock, TResult>(Mock<TMock>, Expression<Func<TMock, TResult>>)' cannot be inferred from the usage.
Try specifying the type arguments explicitly.

I've found the possible solution here - SetupSequence in Moq. But it looks like a workaround.

Community
  • 1
  • 1
Sergey
  • 5,396
  • 3
  • 26
  • 38
  • Show the code where you actually use `SetupSequence` – Nkosi Sep 27 '16 at 10:23
  • Also `OutputManager.WriteMessage` should be virtual or Moq will throw an exception. – Nkosi Sep 27 '16 at 10:29
  • Yes, it's virtual, of course. I've updated my code sample. – Sergey Sep 27 '16 at 10:54
  • What is the desired behavior? – Nkosi Sep 27 '16 at 10:59
  • I'd like using different `Callback(...)` for each `WriteMessage` call. I've added a piece of code to my question. According your answer, I've tried to use an interface instead of a particular class. And it works now. I mean something like this 'var mock = new Mock(); mock.SetupSequence()... ' But it looks like I can't use the '.Callback()' method with `SetupSequence`. Only '.Returns()' is allowed. – Sergey Sep 27 '16 at 11:20
  • That would be correct. SetupSequence allows returns for results and throws for exceptions. it is a setup for a particular use case. anything outside of that would require the workarounds you stated in your question – Nkosi Sep 27 '16 at 11:45
  • OK, thanks, I've got it! – Sergey Sep 27 '16 at 11:47

2 Answers2

3

SetupSequence is used to setup a sequence of returns based on the number of the attempt to use the method that is being set up. An example based on your code which demonstrates what I am talking about :

outputManagerMock.SetupSequence(o => o.WriteMessage(It.IsAny<string>()))
.Returns("Hello for the first attempt!")
.Returns("This is the second attempt to access me!")
.Throws(new Exception());
Akram Qalalwa
  • 103
  • 11
2

If OutputManager is a dependency for other classes then you should consider abstracting that class to make it easier to mock for your tests.

public interface IOutputManager {
    string WriteMessage(string message);
}

That would then mean that the implementation of this interface would look like what you had originally with the addition of the interface.

public class OutputManager : IOutputManager {
    public string WriteMessage(string message) {
        // write a message
    }
}

Given that you originally attempted to mock OutputManager then the assumption here is that it is not the system under test as you typically do not mock the target of the test but rather its dependencies.

So let's assume a dependent class looked something like this.

public class DependentOnIOutputManager {
    private IOutputManager outputManager;

    public DependentOnIOutputManager(IOutputManager outputManager) {
        this.outputManager = outputManager;
    }

    public string SomeMethod(string message) {        
        // write a message
        var output = outputManager.WriteMessage(message);
        //...other code
        return output;
    }
}

Then an example test could look like this.

[TestMethod]
public void Moq_SetupSequence_Example() {
    //Arrange
    var mock = new Mock<IOutputManager>();

    mock.SetupSequence(x => x.WriteMessage(It.IsAny<string>()))
        .Returns("first")
        .Returns("second")
        .Throws<InvalidOperationException>();

    var outputManager = mock.Object;

    var sut = new DependentOnIOutputManager(outputManager);

    //Act
    var first = sut.SomeMethod("1st");

    var second = sut.SomeMethod("2nd");

    Exception e = null;
    try {
        sut.SomeMethod("3rd");
    } catch (InvalidOperationException ex) {
        e = ex;
    }

    //Assert
    Assert.IsNotNull(first);
    Assert.IsNotNull(second);
    Assert.IsNotNull(e);
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472