我有以下课程:
public class Foo
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public Stream TestStream;
public WriteToTestStream()
{ ... }
}
WriteToTestStream
写入TestStream
。
我想写一个单元测试来确保
Prop1
之前,已设置Prop2
和TestStream
TestStream
。在这种情况下,如何为WriteToTestStream
定义模拟?
如何从Mocked函数中操作局部变量(我可以在单元测试中使用局部变量来记住Stream的写入时间/设置属性)?
答案 0 :(得分:2)
执行此操作的一种方法是保留本地状态变量,然后使用Moq的回调来跟踪状态,您可以在回调中Assert
以确保正确的状态。这是一个例子(虽然在NUnit中)。我还假设您确实想要测试一个以正确顺序使用Foo
的SUT(我在这里称为S 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());
}
}
请注意,为了模拟您的Foo
类,需要覆盖方法和属性,例如:所有虚拟或抽象的界面。
以下是这种重构的一个例子:
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 MoqSequences
extension,但我必须承认我还没有使用它。
修改强>
看来MockSequence
(InSequence
)有made it into recent versions of 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());
}
}