简体   繁体   中英

How to verify order of execution in XUnit Moq?

I have the following class:

public class Foo
{
    public int Prop1 { get; set; }
    public string Prop2 { get; set; }

    public Stream TestStream;

    public WriteToTestStream()
    { ... }
}

WriteToTestStream writes to the TestStream .

I want to write a Unit Test to make sure that

  1. Prop1 and Prop2 are being set before writing to the TestStream
  2. The properties should not be accessed once something is written to the TestStream .

How can I define a Mock for WriteToTestStream in this case? How can I manipulate local variables from a Mocked function(I can use local variables in the Unit Test to remember when the Stream was written / properties were set)?

One way to do this is by keeping local state variables and then using Moq's callbacks to track the state, you can Assert within the callbacks to ensure the correct state. Here's an example (Albeit in NUnit). I've also assumed that you actually want to test a SUT which uses Foo in the correct order (I've called the SUT Bar here).

[TestFixture]
public class UnitTest
{
    private bool _prop1Set = false;
    private bool _prop2Set = false;
    private Mock<IFoo> _mockFoo;

    [SetUp]
    public void Setup()
    {
        _mockFoo = new Mock<IFoo>();
        _mockFoo.SetupSet(m => m.Prop1).Callback(i => _prop1Set = true);
        _mockFoo.SetupSet(m => m.Prop2).Callback(s => _prop2Set = true);
        _mockFoo.Setup(m => m.WriteToTestStream())
                .Callback(() => Assert.IsTrue(_prop1Set && _prop2Set));
    }

    [Test]
    public void EnsureInOrder()
    {
        var sut = new Bar(_mockFoo.Object);
        Assert.DoesNotThrow(() => sut.DoSomethingInOrder());
    }

    [Test]
    public void EnsureOutOfOrder()
    {
        var sut = new Bar(_mockFoo.Object);
        Assert.Catch<Exception>(() => sut.DoSomethingOutOfOrder());
    }
}

Note that in order to Mock your Foo class, methods and properties need to be overridable, eg all virtual or with an abstracted interface.

Here's an example of such refactoring:

public interface IFoo
{
    int Prop1 { get; set; }
    string Prop2 { get; set; }
    void WriteToTestStream();
}

public class Foo : IFoo
{
    public int Prop1 { get; set; }
    public string Prop2 { get; set; }
    // Surely this is internal implementation
    private Stream TestStream;
    public void WriteToTestStream() { }
}

public class Bar
{
    private readonly IFoo _foo;
    public Bar(IFoo injectedFoo)
    {
        _foo = injectedFoo;
    }

    public void DoSomethingInOrder()
    {
        _foo.Prop1 = 1;
        _foo.Prop2 = "Baz";
        _foo.WriteToTestStream();
    }

    public void DoSomethingOutOfOrder()
    {
        _foo.WriteToTestStream();
        _foo.Prop2 = "Baz";
        _foo.Prop1 = 1;
    }
}

Another way of doing this could be to use Declan Whelan's MoqSequences extension , although I must admit I haven't used it yet.

Edit

It appears that MockSequence ( InSequence ) has made it into recent versions of Moq , although this might enforce a very rigid sequence, and also appears to require MockBehavior.Strict :

[TestFixture]
public class UnitTest
{
    private Mock<IFoo> _mockFoo;

    [SetUp]
    public void Setup()
    {
        _mockFoo = new Mock<IFoo>(MockBehavior.Strict);
        var seq = new MockSequence();
        _mockFoo.InSequence(seq).SetupSet(m => m.Prop1 = It.IsAny<int>());
        _mockFoo.InSequence(seq).SetupSet(m => m.Prop2 = It.IsAny<string>());
        _mockFoo.InSequence(seq).Setup(m => m.WriteToTestStream());
    }

    [Test]
    public void EnsureInOrder()
    {
        var sut = new Bar(_mockFoo.Object);
        Assert.DoesNotThrow(() => sut.DoSomethingInOrder());
    }

    [Test]
    public void EnsureOutOfOrder()
    {
        var sut = new Bar(_mockFoo.Object);
        Assert.Catch<MockException>(() => sut.DoSomethingOutOfOrder());
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM