【问题标题】:How to keep Mock object in sync with target object如何使 Mock 对象与目标对象保持同步
【发布时间】:2011-07-10 01:11:10
【问题描述】:
我问的是模拟对象管理,不管具体实现(EasyMock、模拟对象等)。
我一直不愿意在我的单元测试中使用 Mock 对象,原因如下:
模拟对象的行为必须反映被模拟对象的行为。如果被模拟对象的行为发生了变化,我们也必须改变模拟对象的行为。如果不这样做,mock 对象的行为就会与真实对象不同步,从而使单元测试变得毫无意义,而且很危险。
我的问题是,
如何使模拟对象与目标对象保持同步?
你如何传播这些变化?
您是否使用任何模拟对象管理技术?
编辑:
更改标题以缩小范围。
【问题讨论】:
标签:
unit-testing
version-control
junit
mocking
【解决方案1】:
定义良好的 API 不应该有这种余地:给定一组输入,被模拟的对象应该只以这些特定的方式表现:行为与接口相关联。如果允许差异,那么您的模拟对象应该测试该对象可以执行的所有不同操作。
您可以通过以下方式降低行为漂移的风险:
【解决方案2】:
我不是专家,但我的想法是模拟对象是为单个测试用例/交互而设计的。如果这种交互发生变化,您显然希望在测试中反映出来。但这不应该破坏一个类的所有模拟对象,因为它们会检查可能仍然有效的不同交互。
【解决方案3】:
如果 ClassA 调用:
AThing aThing = ClassB.GiveMeAThing()
ClassA 永远不应该关心 ClassB 是如何得到那个东西的。因此,存根(StubB)永远不应该关心存根的实际实现如何表现。只有当交互本身发生变化时——返回类型或调用参数——才应该改变 StubB,如果你想让你的代码编译就需要这样做:-)
如果你的 ClassB 开始返回 Nulls 或 Throws 新类型的异常;好吧,那么就需要编写全新的测试,并且可能还有新的存根。
问候,
莫腾
【解决方案4】:
你在正确的轨道上。您不希望每次重构被测系统时都必须更改脆弱的测试。
总的来说,我将测试集中在公共接口上。如果您的公共接口发生更改,您可能需要更改的不仅仅是测试。
我还尽可能地尝试进行状态测试而不是行为测试。所以我通常会使用stubs instead of mocks。 (大多数隔离/模拟框架都允许您创建。)我验证被测系统(或类)的状态,而不是要求模拟对象验证自身。
也就是说,我尽量保持灵活。如果在给定的情况下测试模拟行为是有意义的,我会使用它。如果我需要公开内部信息以获得体面的报道,我会的。
编辑:另见article by Scott Bain。