如何验证XUnit Moq中的执行顺序? [英] How to verify order of execution in XUnit Moq?
问题描述
我有以下课程:
public class Foo
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public Stream TestStream;
public WriteToTestStream()
{ ... }
}
WriteToTestStream
写入TestStream
.
我想编写一个单元测试以确保
I want to write a Unit Test to make sure that
-
在写入
-
Prop1
和Prop2
- 一旦将内容写入
TestStream
,就不应访问属性.
TestStream
之前已设置Prop1
andProp2
are being set before writing to theTestStream
- The properties should not be accessed once something is written to the
TestStream
.
在这种情况下,如何为WriteToTestStream
定义模拟?
如何从Mocked函数操作局部变量(我可以在单元测试中使用局部变量来记住何时编写Stream/设置属性)?
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)?
推荐答案
一种方法是保留局部状态变量,然后使用Moq的回调来跟踪状态,您可以在回调中Assert
以确保正确状态.这是一个示例(尽管在NUnit中).我还假设您实际上要测试按正确顺序使用Foo
的SUT(我在这里将其称为Bar
).
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());
}
}
请注意,要模拟您的Foo
类,方法和属性必须是可重写的,例如全部是虚拟的或具有抽象的接口.
Note that in order to Mock your Foo
class, methods and properties need to be overridable, e.g. 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;
}
}
另一种方法可以使用 Declan Whelan's
Another way of doing this could be to use Declan Whelan's MoqSequences
extension, although I must admit I haven't used it yet.
修改
MockSequence
(InSequence
)似乎已将设置为最新版本的Moq
,尽管这可能会强制执行非常严格的序列,并且似乎还需要 MockBehavior.Strict a>:
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());
}
}
这篇关于如何验证XUnit Moq中的执行顺序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!