【问题标题】:Using mock objects outside of testing, bad practice?在测试之外使用模拟对象,不好的做法?
【发布时间】:2010-09-26 21:47:59
【问题描述】:

我正在从事一个包含大量外部服务消息传递的项目。用稍微“双曲线”的方式来描述它的一个好方法是系统必须向 Flicker API、Facebook API 和 Netflix API 发送消息的应用程序。

为了支持断开连接的场景、日志记录、开发人员可用性、配置等...我已经尝试使用大量使用泛型和表达式树的方法。最终结果如下所示:

Messenger<NetflixApi>.SendCustom( netflix => netflix.RecommendMovie("my message"));

总的来说,我对最终结果感到满意,但我觉得我犯了一个错误,或者在测试和断开连接的场景方面忽略了某个设计主体。

在测试过程中,无论是自动化的、单元的还是基于人的,我都实现了一个对象工厂,它最初使用 DI 在“实时模式”下执行正确的操作,并使用 Mocks 提供一种不在测试模式下做任何事情。

我只看到或读过 Mocks 在纯 TDD 模式下使用,而不是被用作某种愚蠢的对象。我看到的方法将围绕着对我使用的所有 API 所依赖的 HTTP 通信功能进行存根或模拟。

我主要担心的是,我希望连接到的所有不同服务最终将不得不做很多细粒度的工作来代替特定的 HTTP 实现,如果我使用存根方法,我每个都有 3 个类这些服务(IService、ConcreteService、StubService)中的一个,并在实现新方法或更改任何内容时维护这些服务将是真正的 PITA。

在当前的实现中,我使用 Mocks 来免费获得“无菌模式”,几乎不需要为了符合某个测试主体而实现任何额外的东西。

问题是我错过了什么吗?我是否以更...方便的方式使用 Mocks 违反了设计原则?

任何人都可以就如何从许多不同的外部服务中获得无菌模式而无需跳过很多圈子提供任何建议吗?

这个问题有意义吗?

感谢大家的回答。

编辑#1:

我最初的问题并不清楚。任何空对象或模拟对象都将纯粹用于开发/调试/测试环境。在生产中,发送这些消息的代码将是它们的实际实现。

我投票支持每个人,因为对于这个问题似乎有很多不同的解决方案,我将探索每一个。

请不要认为这个问题已经得到回答,我会尽可能多地获得建议。

【问题讨论】:

    标签: c# tdd dependency-injection mocking dependencies


    【解决方案1】:

    我认为您可能需要澄清您的问题。 我不清楚您是在谈论在没有存根或测试期望的情况下在测试中使用测试替身(因此将它们用作假货来满足所需的接口),还是在谈论在生产场景中使用模拟来填充服务不可用(您的断开连接的场景)。

    如果您在测试情况下谈论: 模拟是测试双打。与模拟或存根相比,使用假货进行测试没有任何问题。如果您不需要在特定测试中使用该服务,并且它只是提供给定接口,被测对象依赖于该接口,那么它就可以了。这并没有破坏任何测试原则。

    良好的模拟库正在模糊模拟、存根和伪造之间的界限。

    看看 Martin Fowler 提供的关于不同类型的测试替身的一些信息。 Mocks Aren't Stubs, TestDoubles.

    我真的很喜欢 moq 允许在 dummy、fake、stub 和 mock 对象之间连续统一的方式,而您无需跳过箍来获得特定的行为。 Check it out.

    如果您正在谈论在断开连接的场景中使用模拟来进行生产使用...我会担心的。假货将为您进行的任何调用返回空对象或默认值。这会将复杂性泄漏到所有使用这些服务的代码中(它们需要处理检查空返回值和空数组......)。我认为将您的断开连接/无菌场景编码以处理依赖项本身不可用而不是接受它们的模拟实现并继续,就好像一切正​​常。

    【讨论】:

      【解决方案2】:

      有一种设计模式叫做 Null Object。空对象是实现接口的对象,因此可以在像您这样的场景中使用。

      关于 Null 对象的重要一点是,不要在可能破坏系统的地方返回 null。

      Null Object 的目的是对某些东西进行无效且简单的实现,就像 Mock 一样,但要在生产环境中使用。

      最简单的例子是这样的:

      class Logger{
       private static ILogger _Logger;
      
       static Logger(){
        //DI injection here
        _Logger = new NullLogger(); //or
        _Logger = new TraceLogger();
       }
      }
      
      interface ILogger{
       void Log(string Message);
      }
      
      internal class TraceLogger:ILooger{
       public void Log(string Message){
        //Code here
       }
      }
      
      internal class NullLogger{
       public void Log(string Message){
        //Don't don anything, in purporse
       }
      }
      

      希望对你有帮助

      【讨论】:

      • 当我看到这个主题的第二个主题时,出现了一个红色的“空对象”警报……
      【解决方案3】:

      听起来你可能想看看代理模式。即使与实际服务断开连接,您也想要一些像各种服务一样的东西。因此,您的代码最好与代理而不是真实的东西交谈。然后代理可以根据当前的连接状态做它需要做的任何事情。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-24
        • 2014-06-07
        • 1970-01-01
        相关资源
        最近更新 更多