在测试之外使用模拟对象,是一种不好的做法吗?

5

我正在处理一个项目,其中有大量的外部服务消息传递。一个略微夸张但比较准确的描述是应用程序必须向Flicker API、Facebook API和Netflix API发送消息。

为了支持断开连接的情况、日志记录、开发人员易用性、配置等方面的需求,我尝试使用了一种广泛使用泛型和表达式树的方法。最终的结果如下:

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

总体来说,我对最终结果感到满意,但我的一个担忧是,在测试和断开的场景中,我可能犯了一个错误或忽视了某个设计原则。
在测试期间,无论是自动化、单元还是基于人类的测试,我都使用了一个对象工厂,最初使用 DI 在“实时模式”下执行正确的操作,并使用 Mocks 提供一种类似于无菌信使的东西,在测试模式下不做任何事情。
我只看到或读到过 Mocks 在纯 TDD 模式下使用,并且没有被用作一种愚笨的对象。我看到的方法都是围绕着存根或模拟 HTTP 通信功能来进行的,而所有我使用的 API 都依赖于这些功能。
我的主要担忧是,对于我希望连接到的所有不同服务,我最终会不得不进行大量的细粒度工作来替换特定的 HTTP 实现,如果我使用存根方法,我将为每个服务(IService、ConcreteService、StubService)有 3 个类,当实现新方法或更改任何内容时,维护这些类将是真正的痛苦。
在当前的实现中,我使用 Mocks 几乎能够免费获得“无菌模式”,而无需额外实施任何内容来符合某个测试原则。
问题是,我有什么遗漏吗?我使用 Mocks 是在更方便的方式中违反了设计原则吗?
有人可以就如何在许多不同的外部服务中获得无菌模式提供任何建议吗,而不需要跳过很多障碍?
这个问题有意义吗?
感谢所有的回答。
编辑 #1:
我在原始问题中没有表述清楚。任何空值或模拟对象都仅用于开发/调试/测试环境。在生产中发送这些消息的代码将是它们的实际实现。
我投票支持每个人,因为似乎有很多不同的解决方法,我将探索每一种方法。
请不要认为这个问题已经得到答复,我会非常感激尽可能多的建议。
3个回答

6

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

Null Object 的重要之处在于不要在可能破坏系统的地方返回 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
 }
}

我希望这能对您有所帮助


我一看到这个主题,立刻就警觉起来了,红色的“空对象”警报亮了起来... - abyx

4
我认为你需要澄清你的问题。我不确定你是在谈论在测试中使用测试替身而没有存根或测试期望(因此将它们用作假物来满足所需界面),还是在谈论在生产场景中使用模拟来填充当前不可用的服务(您的断开连接场景)。
如果你在谈论测试情况: 模拟是测试替身。使用假物进行测试而不是模拟或存根并没有错。如果您不需要在特定测试中使用服务,而只是为了提供对象所依赖的给定接口,则可以使用该服务。这不会违反任何测试原则。
好的模拟库正在模糊模拟、存根和伪造之间的界限。
查看一些马丁·福勒关于不同类型的测试替身的信息。模拟不是存根, TestDoubles.

我真的很喜欢moq的方式,它允许在不需要跳过特定行为的情况下,在虚拟对象、伪造对象、存根和模拟对象之间建立一个连续体。看看吧

如果您谈论在生产环境中使用模拟对象...我会感到担忧。伪造对象将返回空对象或默认值,对于您进行的任何调用。这将使所有消费这些服务的代码变得复杂(它们需要处理检查空返回值和空数组...)。我认为,在您的断开/无菌场景中,编写处理依赖性本身不可用的代码比接受它们的模拟实现并继续仿佛一切正常更有意义。


4

我认为你可能需要看一下代理模式。即使与实际服务断开连接,您也希望有某些东西像各种服务一样运行。因此,您的代码最好与代理而不是真实的服务交互。然后,代理可以根据当前连接状态执行所需的任何操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接