Let's suppose that your _store
implements the following interface:
public interface IStore
{
IStore SetInput(string input);
IStore Method1();
IStore Method2();
IStore Method3();
}
For the sake of simplicity here is class that makes use of an IStore
implementation:
public class SystemUnderTest
{
private readonly IStore _store;
public SystemUnderTest(IStore store)
{
_store = store;
}
public void MyAction()
{
_store
.SetInput("input1")
.Method1()
.Method2()
.Method3();
}
}
So, in order to make sure that the actions are executing in this particular order we can make use of the MockSequence
(ref). Here is a sample unit test for this:
[Fact]
public void GivenAFlawlessStore_WhenICallMyAction_ThenItCallsTheMethodsOfTheStoreInAParticularOrder()
{
//Arrange
var storeMock = new Mock<IStore>();
var expectedSequence = new MockSequence();
storeMock.InSequence(expectedSequence).Setup(x => x.SetInput(It.IsAny<string>()))
.Returns(storeMock.Object);
storeMock.InSequence(expectedSequence).Setup(x => x.Method1())
.Returns(storeMock.Object);
storeMock.InSequence(expectedSequence).Setup(x => x.Method2())
.Returns(storeMock.Object);
storeMock.InSequence(expectedSequence).Setup(x => x.Method3())
.Returns(storeMock.Object);
var SUT = new SystemUnderTest(storeMock.Object);
//Act
SUT.MyAction();
//Assert
storeMock.Verify(store => store.SetInput("input1"), Times.Once);
}
If you change the order either in the unit test or inside the MyAction
method it will fail with a NullReferenceException
, because it can't call the next method on a null.
Change the order inside the MyAction
public void MyAction()
{
_store
.SetInput("input1")
.Method2()
.Method1() //NullReferenceException
.Method3();
}
Change the order of the MockSequence
//Arrange
var storeMock = new Mock<IStore>();
var expectedSequence = new MockSequence();
storeMock.InSequence(expectedSequence).Setup(x => x.SetInput(It.IsAny<string>()))
.Returns(storeMock.Object);
storeMock.InSequence(expectedSequence).Setup(x => x.Method1())
.Returns(storeMock.Object);
storeMock.InSequence(expectedSequence).Setup(x => x.Method3())
.Returns(storeMock.Object);
storeMock.InSequence(expectedSequence).Setup(x => x.Method2())
.Returns(storeMock.Object);
In order words your Setup
calls won't have any effect in advance. When you call the SetInput
from the MyAction
the first setup will be executed in the sequence.
If you try to call Method2
before Method1
's Setup
call then it will fail.