3

例子:

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

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

  1. IsNew正在读取属性
  2. 或者Create()已经Update()被调用
4

2 回答 2

3

突然想到:验证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 时,您指示模拟期望该特定行为。如果在调用验证时没有满足该期望,它将抛出异常,从而使测试失败。

于 2009-07-08T17:57:13.903 回答
3

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

您所要做的就是将一个局部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);

会有两个测试。一个将属性设置IsNewtrue,一个将其设置为false

于 2009-07-08T20:59:40.617 回答