例子:
public bool Save(MyObj instance)
{
if (instance.IsNew)
{
this.repository.Create(instance);
}
else
{
this.repository.Update(instance);
}
}
如何在 Moq 中创建一个测试来验证:
IsNew
正在读取属性- 或者
Create()
已经Update()
被调用
例子:
public bool Save(MyObj instance)
{
if (instance.IsNew)
{
this.repository.Create(instance);
}
else
{
this.repository.Update(instance);
}
}
如何在 Moq 中创建一个测试来验证:
IsNew
正在读取属性Create()
已经Update()
被调用突然想到:验证IsNew
正在读取属性:
var mock = new Mock<MyObj>();
mock.Setup(m => m.IsNew).Returns(true).Verifiable();
//...
sut.Save(mock.Object);
//...
mock.Verify();
在上面的示例中,该IsNew
属性将返回true
,因此将采用 Create 路径。
要验证是否调用了 Create 或 Update 方法,您需要对该功能进行一些挂钩。看起来 Repository 是一个静态类,在这种情况下你不能用 Test Double 替换它,但我可能以错误的方式阅读你的代码......如果你可以用 Test Double (Mock) 替换它,您可以使用与上述相同的原理。
如果在调用 Save 方法后可以检查存储库的状态,则可以通过基于状态的测试来判断遵循了两个代码路径中的哪一个。
如果两个代码路径的结果之间没有外部可观察到的差异,最好不要测试这个特定的实现细节。它可能会将您引向一种称为 Overspecified Test 的反模式——您可以在优秀的书xUnit Test Patterns中阅读有关此反模式和许多其他单元测试相关内容的更多信息。
编辑: 测试存储库可以以相同的方式完成:
var myObjMock = new Mock<MyObj>();
myObjMock.Setup(m => m.IsNew).Returns(true);
var repositoryMock = new Mock<Repository>();
repositoryMock.Setup(m => m.Create(myObjMock.Object)).Verifiable();
var sut = new SomeClass(repositoryMock.Object);
sut.Save(myObjMock.Object);
repositoryMock.Verify();
对 Verifiable 的调用是关键。没有它,Moq 的默认行为是让开,尽其所能,尽可能不抛出任何异常。
当您调用 Verifiable 时,您指示模拟期望该特定行为。如果在调用验证时没有满足该期望,它将抛出异常,从而使测试失败。
不幸的是,我自己有一个解决方案。
您所要做的就是将一个局部int
变量设置为 0,然后模拟递增它。最后,您必须检查其名称是否大于 0(或恰好为 1,具体取决于问题)。
// Arrange
int count = 0;
Mock<Repository> mock = new Mock<Repository>();
mock.Setup<bool>(m => m.Create(It.IsAny<MyObj>())).Callback(() => count++);
mock.Setup<bool>(m => m.Update(It.IsAny<MyObj>())).Callback(() => count++);
// Act
...
// Assert
Assert.AreEqual(count, 1);
会有两个测试。一个将属性设置IsNew
为true
,一个将其设置为false
。