我有以下类:
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
在这种情况下? 如何操作从嘲笑函数的局部变量(我可以使用局部变量在单元测试时要记住的流写入/属性设定)?
要做到这一点的方法之一是保持本地状态变量,然后用起订量的回调跟踪状态下,可以Assert
的回调内,以确保正确的状态。 下面是一个例子(尽管在NUnit的)。 我还以为你真的想测试它采用了SUT Foo
以正确的顺序(我称为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());
}
}
需要注意的是,为了嘲笑你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;
}
}
这样做的另一种方法可以是使用迪克兰惠兰的 MoqSequences
扩展 ,尽管我必须承认我还没有使用它。
编辑
看来, MockSequence
( InSequence
),已使它成为最新版本的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());
}
}