7

所以我正在为我的单元测试使用模拟框架(Moq),并且想知道什么时候应该使用模拟框架?

以下两个测试之间的优点/缺点是什么:

public class Tests
{
    [Fact]
    public void TestWithMock()
    {
        // Arrange
        var repo = new Mock<IRepository>();

        var p = new Mock<Person>();
        p.Setup(x => x.Id).Returns(1);
        p.Setup(x => x.Name).Returns("Joe Blow");
        p.Setup(x => x.AkaNames).Returns(new List<string> { "Joey", "Mugs" });
        p.Setup(x => x.AkaNames.Remove(It.IsAny<string>()));

        // Act
        var service = new Service(repo.Object);
        service.RemoveAkaName(p.Object, "Mugs");

        // Assert
        p.Verify(x => x.AkaNames.Remove("Mugs"), Times.Once());
    }

    [Fact]
    public void TestWithoutMock()
    {
        // Arrange
        var repo = new Mock<IRepository>();

        var p = new Person { Id = 1, Name = "Joe Blow", AkaNames = new List<string> { "Joey", "Mugs" } };

        // Act
        var service = new Service(repo.Object);
        service.RemoveAkaName(p, "Mugs");

        // Assert
        Assert.True(p.AkaNames.Count == 1);
        Assert.True(p.AkaNames[0] == "Joey");
    }
}
4

6 回答 6

11

使用模拟对象来真正创建一个单元测试——一个假设所有依赖项都正常运行的测试,你只想知道 SUT(被测系统——一种说你正在测试的类的奇特方式)是否有效.

模拟对象有助于“保证”您的依赖项正确运行,因为您创建了那些产生您配置的结果的依赖项的模拟版本。那么问题就变成了,如果你正在测试的一个类在其他一切都“工作”时表现得应该如此。

当您测试具有缓慢依赖关系的对象(如数据库或 Web 服务)时,模拟对象尤其重要。如果您要真正访问数据库或进行真正的 Web 服务调用,您的测试将需要更多的时间来运行。如果只多花几秒钟,这是可以忍受的,但是当您在持续集成服务器中运行数百个测试时,这会非常快地加起来并削弱您的自动化。

这就是真正使模拟对象变得重要的原因——减少构建-测试-部署周期时间。确保您的测试快速运行对于高效的软件开发至关重要。

于 2013-10-30T05:56:04.570 回答
2

我使用一些规则来编写单元测试。

  1. 如果我的被测系统 (SUT) 或被测对象具有依赖关系,那么我会全部模拟它们。
  2. 如果我测试一个返回结果的方法,那么我只检查结果。如果依赖项作为方法的参数传递,则应模拟它们。(见1)
  3. 如果我测试 'void' 方法,那么验证模拟是测试的最佳选择。

Martin Fowler 有一篇旧文章Mocks Aren't Stubs

在第一个测试中,您使用 Mock,而在第二个测试中,您使用 Stub。

我还看到一些导致您的问题的设计问题。

如果允许AkaNameAkaNames集合中删除,则可以使用存根检查人员的状态。如果您将特定方法添加void RemoveAkaName(string name)Person类中,则应使用模拟来验证其调用。并且逻辑RemoveAkaName应该作为Person类测试的一部分进行测试。

我会为你的代码使用存根product和模拟。repository

于 2013-10-31T10:32:04.607 回答
1

Mock 用于测试不能单独运行的对象。假设函数 A 依赖于函数 B,要对函数 A 进行单元测试,我们甚至会测试函数 B。通过使用 mock,您可以模拟函数 B 的功能,并且测试可以只关注函数 A。

于 2013-10-30T05:43:45.313 回答
1

模拟框架对于模拟被测代码中的集成点很有用。我认为您的具体示例不是模拟框架的良好候选者,因为您已经可以将依赖项(Person)直接注入代码中。在这种情况下,使用模拟框架实际上会使它复杂化。

一个更好的用例是,如果您有一个存储库调用数据库。从单元测试的角度来看,最好模拟 db 调用并返回预定数据。这样做的主要优点是消除了对现有数据的依赖,还有性能,因为 db 调用会减慢测试速度。

于 2013-10-30T18:28:27.607 回答
1

模拟有很多好处,尤其是在敏捷编程中,许多快速发布周期意味着系统结构和真实数据可能不完整。在这种情况下,您可以模拟存储库以模拟生产代码,以便继续在 ui 或服务上工作。这通常与 Ninject 之类的 IoC 机制相辅相成,以简化向真实存储库的切换。您给出的两个示例是相同的,没有任何其他上下文,我会说这是它们之间的选择问题。moq 中的 fluent api 可能更容易阅读,因为它是一种自我记录。这是我的看法;)

于 2013-10-30T05:28:25.067 回答
1

mocking 框架是用来去除依赖的,所以单元测试会集中在要测试的“Unit”上。在你的情况下,这个人看起来像一个简单的实体类,不需要使用 Mocking 。

于 2013-10-30T05:21:58.270 回答