【问题标题】:Why Create Mock Objects?为什么要创建模拟对象?
【发布时间】:2010-11-27 16:39:31
【问题描述】:

在最近的一次采访中,有人问我为什么要创建模拟对象。我的回答是这样的,“拿一个数据库——如果你正在编写测试代码,你可能不希望该测试实时连接到将执行实际操作的生产数据库。”

从回复来看,我的答案显然不是面试官想要的。有什么更好的答案?

【问题讨论】:

    标签: unit-testing mocking testing


    【解决方案1】:

    我会这样总结:

    • 隔离 - 您只能测试一个方法,独立于它调用的内容。你的测试变成了一个真正的单元测试(最重要的恕我直言)
    • 减少测试开发时间 - 通常使用模拟比创建一个完整的类来帮助您测试更快
    • 即使您没有实现所有依赖项,它也可以让您进行测试 - 例如,您甚至不需要创建存储库类,您就可以测试一个类使用此存储库
    • 让您远离外部资源 - 从某种意义上说,您无需访问数据库、调用网络服务、阅读文件、发送电子邮件或从信用卡中收取费用,等等……

    在一次采访中,我建议在开发人员使用 依赖注入 时加入 mock,因为它可以让您拥有更多控制权,并且更轻松地构建测试。

    【讨论】:

    • 当然,在 Mock Objects 的 bad 方面,当真正的类接口发生变化时,有人不得不四处寻找和更新 Mocks,与此同时所有那些“成功”的测试都是谎言。
    • 如果您的其中一项测试包括验证接口未更改,则可以缓解这种情况。那个测试失败了,你知道你的模拟是错误的。
    • 但这并不能改变界面订阅者坏掉的事实。
    • @Gutzofter 根据您使用的模拟库和 IDE,在更改接口时,它还会更改对该接口的模拟调用。
    • @Samual - 我花了很多时间思考这个问题。如果您有很多订阅者到界面(包括动态语言)并且它发生了变化,那么您有很多损坏的订阅者(测试)。我可能不得不重读一些 Martin Fowler 才能更好地了解这一点。您是否有任何针对此问题的参考资料?
    【解决方案2】:

    采用稍微不同的方法(因为我认为上面已经很好地介绍了模拟):
    “获取一个数据库——如果您正在编写测试代码,您可能不希望该测试实时连接到将执行实际操作的生产数据库。”

    IMO 说明示例使用的一种不好的方式。在使用或不使用模拟的测试期间,您永远不会“将其连接到产品数据库”。每个开发人员都应该有一个开发人员本地数据库来进行测试。然后您将继续使用测试环境数据库,然后可能是 UAT,最后是 prod。您不是为了避免使用实时数据库而进行模拟,而是为了使不直接依赖于数据库的类不需要您设置数据库。

    最后(我接受我可能会在这方面得到一些 cmets)IMO 开发人员本地数据库是在单元测试期间命中的有效对象。您应该只在测试直接与数据库交互的代码时使用它,并在测试间接访问数据库的代码时使用模拟。

    【讨论】:

      【解决方案3】:

      我将在这里采取不同的方向。 Stubbing/Faking 做了上面提到的所有事情,但也许面试官认为 mock 是一个伪造的对象,会导致测试通过或失败。我基于xUnit terminology。这可能会引发一些关于state testing verses behavior / interaction testing 的讨论。

      他们可能一直在寻找的答案是:模拟对象与存根不同。存根模拟被测方法的依赖关系。存根不应导致测试失败。模拟会执行此操作并且还会检查调用方式和时间。模拟根据潜在行为导致测试通过或失败。这样做的好处是在测试期间减少了对数据的依赖,但将其与方法的实现更紧密地联系在一起。

      当然这是猜测,他们更可能只是想让您描述存根和 DI 的好处。

      【讨论】:

        【解决方案4】:

        这里是a few situations where mocking is indispensable:

        1. 测试 GUI 交互时
        2. 当您测试 Web 应用时
        3. 在测试与硬件交互的代码时
        4. 在测试旧版应用时

        【讨论】:

          【解决方案5】:

          只是为了补充这里的好答案,模拟对象用于自上而下或自下而上的结构化编程(OOP 也是如此)。它们用于为上层模块(GUI、逻辑处理)提供数据或充当模拟输出。

          考虑自上而下的方法:您首先开发一个 GUI,但 GUI 应该有数据。因此,您创建了一个模拟数据库,它只返回一个 std::vector 数据。您已经定义了关系的“合同”。谁在乎数据库对象内部发生了什么——只要我的 GUI 列表得到一个 std::vector 我就很高兴。这可以提供模拟用户登录信息,无论您需要什么让 GUI 正常工作。

          考虑一种自下而上的方法。您编写了一个解析器,它读取分隔的文本文件。你怎么知道它是否有效?您为这些对象编写一个模拟“数据接收器”并将数据路由到那里以验证(尽管通常)数据是否被正确读取。上一层的模块可能需要 2 个数据源,但你只写了一个。

          在定义模拟对象的同时,您还定义了合同关系的方式。这通常用于测试驱动编程。您编写测试用例,使用模拟对象使其工作,并且通常模拟对象的接口成为最终接口(这就是为什么在某些时候您可能希望将模拟对象的接口分离成纯抽象类) .

          希望对你有帮助

          【讨论】:

            【解决方案6】:

            在进行单元测试时,每个测试都旨在测试单个对象。然而,系统中的大多数对象都会有其他与之交互的对象。模拟对象是这些其他对象的虚拟实现,用于隔离被测对象。

            这样做的好处是任何失败的单元测试通常会将问题隔离到被测对象。在某些情况下,问题出在模拟对象上,但这些问题应该更容易识别和修复。

            也可以为模拟对象编写一些简单的单元测试。

            它们通常用于创建模拟数据访问层,以便可以在与数据存储隔离的情况下运行单元测试。

            其他用途可能是在 MVC 模式中测试控制器对象时模拟用户界面。这允许更好地自动化测试可以在某种程度上模拟用户交互的 UI 组件。

            一个例子:

            public interface IPersonDAO
            {
                Person FindById(int id);
                int Count();
            }
            
            public class MockPersonDAO : IPersonDAO
            {
                // public so the set of people can be loaded by the unit test
                public Dictionary<int, Person> _DataStore;
            
                public MockPersonDAO()
                {
                    _DataStore = new Dictionary<int, Person>();
                }
            
                public Person FindById(int id)
                {
                    return _DataStore[id];
                }
            
                public int Count()
                {
                    return _DataStore.Count;
                }
            }
            

            【讨论】:

              【解决方案7】:

              在团队中工作时,模拟对象/函数也很有用。如果您正在处理代码库的一部分,该部分代码库依赖于其他人负责的代码库的不同部分——仍在编写或尚未编写——模拟对象/函数在给你一个预期的输出,这样你就可以继续你的工作,而不必等待其他人完成他们的工作。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-01-23
                • 2017-04-07
                • 2013-02-24
                相关资源
                最近更新 更多