验证在单元测试中是否调用了一个方法或另一个方法

时间:2009-07-08 17:41:18

标签: unit-testing mocking moq

示例:

public bool Save(MyObj instance)
{
    if (instance.IsNew)
    {
        this.repository.Create(instance);
    }
    else
    {
        this.repository.Update(instance);
    }
}

如何在Moq中创建一个验证:

的测试
  1. 正在阅读属性IsNew
  2. 已调用Create()Update()

2 个答案:

答案 0 :(得分:3)

脱离我的头顶: 验证是否正在读取IsNew属性:

var mock = new Mock<MyObj>();
mock.Setup(m => m.IsNew).Returns(true).Verifiable();
//...
sut.Save(mock.Object);
//...
mock.Verify();

在上面的示例中,IsNew属性将返回true,因此将创建“创建”路径。

要验证是否已调用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时,您会指示模拟器期望该特定行为。如果在调用Verify时未满足该期望,则会抛出异常,从而使测试失败。

答案 1 :(得分:3)

不幸的是我自己有一个解决方案。

您所要做的就是拥有一个设置为0的本地int变量,然后模拟增量。最后,您必须检查其名称是否大于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