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

时间:2008-12-15 02:00:51

标签: c# tdd dependency-injection mocking dependencies

我正在开展一个有很多外部服务消息传递的项目。仅以稍微“双曲线”的方式描述它的一种好方法是系统必须向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:

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

我投票给大家,因为这个问题似乎有很多不同的解决方案,我会探索每个问题。

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

3 个答案:

答案 0 :(得分:6)

有一种名为Null Object的设计模式。 null对象是实现Interface的对象,因此它可以在像您这样的场景中使用。

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

Null对象的目的是有一个虚空和简单的实现,就像模拟一样,但是要在生产环境中使用。

最简单的例子是这样的:

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
 }
}

我希望这可以帮到你

答案 1 :(得分:4)

我认为您可能需要澄清您的问题。 我不清楚你是否在谈论在测试中使用测试双打而没有存根或测试期望(因此将它们用作伪造以满足所需的接口)或者你是否在谈论在生产场景中使用模拟以填补服务不可用(您的断开连接方案)。

如果您在测试情况中谈论: 模拟是测试双打。使用假货而不是模拟或存根进行测试没有任何问题。如果您不需要在特定测试中使用该服务,那么只是提供给定接口,被测对象具有依赖性,那就没问题了。这并没有打破任何测试校长。

好的模拟库模糊了模拟,存根和假货之间的界限。

查看Martin Fowler关于不同类型测试双打的一些信息。 Mocks Aren't StubsTestDoubles

我非常喜欢moq允许虚拟对象,假对象,存根对象和模拟对象之间连续的方式,而不需要跳过箍来获取特定的行为。 Check it out

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

答案 2 :(得分:4)

听起来我可能想看一下代理模式。即使与实际服务断开连接,您也希望能够像各种服务一样工作。因此,您的代码最好不要与代理交谈而不是真实的代理。然后代理可以根据当前连接状态执行任何操作。