我知道这个问题是一场宗教战争的一部分,但我有以下情况:
我有一个对象Responder
,它调用对象Updater
上的方法来响应不同的事件。我最近以这种方式分离了测试:Updater
方法本身的基于状态的测试,以及调用它的Responder
的基于行为的测试。也就是说,我在Updater
测试中模拟Responder
,只是为了确保它被调用。
我是否还应该测试Responder
测试中应该更新的对象的状态而不是模拟Updater
?我喜欢我所做的,因为它需要较少的设置,似乎更好地隔离测试。但是,这似乎将Responder
的实施和预期行为与Updater
联系起来。那太脆了吗?这是一个简化的例子。
答案 0 :(得分:9)
如果我确实理解了你的问题,你至少需要两个级别的测试:
单元测试,您尝试仅测试一个类并模拟所有依赖项(因此在您的情况下,需要在此处模拟Updater)。这些测试可以帮助您开发代码(特别是如果您使用的是TDD),请确保该类的行为符合设计,甚至可以记录此类的行为方式。几乎每个班级都应该进行单元测试。但是,正如您所注意到的那样即使您有100%的测试覆盖率,也无法保证您的程序能够正常运行甚至启动!
验收,集成和端到端测试 - 这些测试涵盖整个应用程序或大型模块,并测试所有内容是否一致。通常,您不在此级别使用模拟(您可能会存根整个模块/ Web服务,具体取决于上下文)。这些测试不必测试每个实现细节(也不应该),因为这是通过单元测试完成的。他们确保一切都正确连线并一起工作。在你的情况下,你不会在这里模拟更新。
总而言之,我认为您确实需要同时对两者进行适当的测试。
答案 1 :(得分:0)
我想这取决于你试图测试的内容和方式。
如果您在Updater类中没有任何依赖项,那么在调用测试方法之后,没有其他方法可以测试它的状态。如果存在某种依赖关系,您可以测试它应该做什么的行为(在其他类上调用其他方法)
对于Responder类,反过来也可能是这样。您可以测试它的状态,或者您可以像刚才那样测试它的行为。测试它的状态你可能会使用更新程序的真实实现,而不是模拟它,或者在它的地方使用存根,只做最小的。
当主要测试行为和嘲笑很多东西时,更有必要接受(回归/集成/端到端)测试来备份你的单元测试,就像Grzenio提到的那样。 (看来他是一个“模仿者”(行为)测试者)
当主要进行基于状态的测试时,您使用更多真实的实现及其交互,并且支持这些实现的需求不那么重要。但这绝不是完全抛弃它们的一种方式,只是你已经在测试中有一些回归/整合。
我认为你不应该把测试分成太多,因为这可能会让事情更容易陷入裂缝之间。
免责声明:我不是tdd大师,我不提倡其中一个。关于经典与行为的好消息可以在网上找到。 Martin Fowler在这个主题上有一个很好的起点:Mocks Aren't Stubs
我目前正在阅读以测试为导向的面向对象的成长软件,并且倾向于围墙的模仿者。但我认为双方都有一个小的灰色区域。