行为与基于状态的测试

时间:2010-08-18 16:28:49

标签: unit-testing mocking

我知道这个问题是一场宗教战争的一部分,但我有以下情况: 我有一个对象Responder,它调用对象Updater上的方法来响应不同的事件。我最近以这种方式分离了测试:Updater方法本身的基于状态的测试,以及调用它的Responder的基于行为的测试。也就是说,我在Updater测试中模拟Responder,只是为了确保它被调用。

我是否还应该测试Responder测试中应该更新的对象的状态而不是模拟Updater?我喜欢我所做的,因为它需要较少的设置,似乎更好地隔离测试。但是,这似乎将Responder的实施和预期行为与Updater联系起来。那太脆了吗?这是一个简化的例子。

2 个答案:

答案 0 :(得分:9)

如果我确实理解了你的问题,你至少需要两个级别的测试:

  1. 单元测试,您尝试仅测试一个类并模拟所有依赖项(因此在您的情况下,需要在此处模拟Updater)。这些测试可以帮助您开发代码(特别是如果您使用的是TDD),请确保该类的行为符合设计,甚至可以记录此类的行为方式。几乎每个班级都应该进行单元测试。但是,正如您所注意到的那样即使您有100%的测试覆盖率,也无法保证您的程序能够正常运行甚至启动!

  2. 验收,集成和端到端测试 - 这些测试涵盖整个应用程序或大型模块,并测试所有内容是否一致。通常,您不在此级别使用模拟(您可能会存根整个模块/ Web服务,具体取决于上下文)。这些测试不必测试每个实现细节(也不应该),因为这是通过单元测试完成的。他们确保一切都正确连线并一起工作。在你的情况下,你不会在这里模拟更新。

  3. 总而言之,我认为您确实需要同时对两者进行适当的测试。

答案 1 :(得分:0)

我想这取决于你试图测试的内容和方式。

如果您在Updater类中没有任何依赖项,那么在调用测试方法之后,没有其他方法可以测试它的状态。如果存在某种依赖关系,您可以测试它应该做什么的行为(在其他类上调用其他方法)

对于Responder类,反过来也可能是这样。您可以测试它的状态,或者您可以像刚才那样测试它的行为。测试它的状态你可能会使用更新程序的真实实现,而不是模拟它,或者在它的地方使用存根,只做最小的。

当主要测试行为和嘲笑很多东西时,更有必要接受(回归/集成/端到端)测试来备份你的单元测试,就像Grzenio提到的那样。 (看来他是一个“模仿者”(行为)测试者)

当主要进行基于状态的测试时,您使用更多真实的实现及其交互,并且支持这些实现的需求不那么重要。但这绝不是完全抛弃它们的一种方式,只是你已经在测试中有一些回归/整合。

我认为你不应该把测试分成太多,因为这可能会让事情更容易陷入裂缝之间。

免责声明:我不是tdd大师,我不提倡其中一个。关于经典与行为的好消息可以在网上找到。 Martin Fowler在这个主题上有一个很好的起点:Mocks Aren't Stubs

我目前正在阅读以测试为导向的面向对象的成长软件,并且倾向于围墙的模仿者。但我认为双方都有一个小的灰色区域。