【问题标题】:Verify that either one method or the other was invoked in a unit test验证是否在单元测试中调用了一种方法或另一种方法
【发布时间】:2009-07-08 17:41:17
【问题描述】:

示例:

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

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

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

【问题讨论】:

    标签: unit-testing mocking moq


    【解决方案1】:

    在我的脑海中: 验证正在读取 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 方法后检查存储库的状态,则可以通过基于状态的测试来判断遵循了两个代码路径中的哪一个。

    如果两个代码路径的结果之间没有外部可观察差异,那么最好不要测试这个特定的实现细节。它可能会引导你走向一种称为过度指定测试的反模式——你可以在优秀的书籍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 时未满足该期望,则会抛出异常,从而导致测试失败。

    【讨论】:

    • 我更改了存储库的代码。这只是代码。它并不意味着是单例存储库。实际上我有一个接口,所以我可以在我的对象实例化(构造函数重载)处注入我的存储库。
    • 我在测试中添加了 Verifiable()。但是当期望没有得到满足时没有区别。我总是遇到同样的异常。
    【解决方案2】:

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

    您所要做的就是将本地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

    【讨论】:

    • 在下面查看我的更新答案。一般来说,您应该尽可能明确地表达期望。使用最小起订量,这可以使用 Verifiable 和 Verify 方法有效地完成。那会更明确。对于 Moq 的普通用户来说,这也是他们所期望的,因此以这种方式编写的测试将比您的解决方案更清晰。
    • 之前没用过 Verifiable。伟大的。我会尝试看看有什么不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-24
    • 2014-04-18
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    相关资源
    最近更新 更多