5

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

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

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

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

4

3 回答 3

6

有一种设计模式称为 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
 }
}

我希望这可以帮助你

于 2008-12-15T05:25:30.623 回答
4

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

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

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

看看 Martin Fowler 提供的关于不同类型的测试替身的一些信息。模拟不是存根TestDoubles

我真的很喜欢起订量允许虚拟对象、假对象、存根对象和模拟对象之间的连续统一体的方式,而您无需跳过箍来获得特定的行为。检查出来

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

于 2008-12-15T04:10:01.947 回答
4

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

于 2008-12-15T05:57:51.090 回答