简体   繁体   English

如何验证XUnit Moq中的执行顺序?

[英]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 . WriteToTestStream写入TestStream

I want to write a Unit Test to make sure that 我想编写一个单元测试以确保

  1. Prop1 and Prop2 are being set before writing to the TestStream Prop1Prop2被写入到之前设置TestStream
  2. The properties should not be accessed once something is written to the TestStream . 一旦将某些内容写入TestStream就不应访问这些属性。

How can I define a Mock for WriteToTestStream in this case? 在这种情况下,如何定义WriteToTestStream的模拟? 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)? 如何从Mocked函数操作局部变量(可以在单元测试中使用局部变量来记住何时编写Stream /设置属性)?

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. 要做到这一点的方法之一是保持本地状态变量,然后用起订量的回调跟踪状态下,可以Assert的回调内,以确保正确的状态。 Here's an example (Albeit in NUnit). 这是一个示例(尽管在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). 我还假设您实际上是要测试以正确顺序使用Foo的SUT(在这里我将其称为SUT Bar )。

[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. 请注意,为了模拟Foo类,方法和属性必须是可重写的,例如,所有虚拟方法或具有抽象接口的方法和属性。

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. 做到这一点的另一种方法可能是使用Declan Whelan的 MoqSequences扩展 ,尽管我必须承认我还没有使用过它。

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 : 看起来MockSequenceInSequence )已将其添加到Moq最新版本中 ,尽管这可能会强制执行非常严格的序列,并且似乎还需要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());
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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