目前我正在编写一个测试驱动的项目,并且我坚持测试以下行为。
我有一个名为Menu
的接口,可以通过addEntry
方法添加动态条目。还有另一个包含Menu
对象的类。我们称之为MenuPresenter
。当调用特定方法(例如someAction(string title)
)时,MenuPresenter
应该使用收到的标题实例化Entry
对象,并将其添加到Menu
对象。 Entry
对象的实例化发生在MenuPresenter
内的工厂方法中。
测试的行为应为WhenSomeActionIsCalledShouldAddAnEntryContainingTitleToMenu
但我没有找到正确的方法来编写测试。我想出了测试它的两种主要可能性,但实际上我不喜欢它们,因为(后面)提到的缺点。
MenuSpy
继承的Menu
,其中包含getAddedEntry
- 方法。像这样你可以提取添加的Entry
并检查对象的状态EXPECT_TRUE(entry->getTitle() == title);
缺点:要检查Entry
对象的状态,我要么使用getter-Methods扩展接口的API,这些方法仅用于测试原因或使用公共成员变量。这允许每个客户端访问Entry
的每个实现的内部结构。因此,客户与内部结构相结合。
EntryFactory
界面扩展系统
makeEntry(std::string title)
- 方法。像这样可以实现EntryFactorySpy
并且可以检查是否使用正确的参数(标题)调用makeEntry
- 方法。在另一个测试中,我们可以实现一个返回特定EntryFactoryStub
对象的Entry
。然后,我们可以检查(通过MenuSpy
)MenuPresenter
是否已将收到的条目从工厂添加到菜单中。然后,在工厂的单元测试中测试Entry
对象的实例化。 缺点:因为我测试了工厂makeEntry
- 方法的调用,所以修复了使用工厂创建条目的算法。测试与MenuPresenter
的内部结构紧密耦合。更改算法(例如,现在使用工厂方法将破坏测试,而不会导致应用程序的预期行为中断。
对于应用程序的行为,如果MenuPresenter
创建Entry
本身,如果它使用EntryFactory
,那么它应该是不重要的。这就是为什么我更喜欢第一种方式。但是,由于测试原因,我不希望Entry
的客户端与Entry
的内部结构耦合。
这只是我问题的一个例子。实际上,该条目不仅使用字符串创建。它将其他复杂对象作为shared_ptr。这是另一个原因,为什么我不想使用公共getter方法。像这样可以从Entry
中提取复杂对象并进行更改(是的,我可以给出一个const shared_ptr,但这对我来说似乎不是一个好习惯。)
问题是,是否有人知道测试模式或解决方案来解决我的问题?意味着测试是否将Entry
对象添加到Menu
对象而不是紧密耦合到算法或Entry
的内部结构?
答案 0 :(得分:0)
我是一名java开发人员,我从不在c ++中使用TDD,但我可以描述你想要测试的内容,如上所述。
在Classic TDD中,MenuPresenter
应该更像是一个迷你集成测试,正如Martin Fowler在Test Isolation中所说的那样。
本质上,经典的xunit测试不仅仅是单元测试,还包括小型集成测试。因此,许多人喜欢这样的事实:客户端测试可能会捕获对象的主要测试可能错过的错误,特别是探测类交互的区域。
在经典TDD中尽可能使用真实对象,如果在测试MenuPresenter
时使用真实对象,则使用Test Double。Entry
。因此,问问自己在Menu
中添加Menu
时会发生什么预期效果。测试Entry
将添加标题Entry
只是没有意义的,你应该测试添加Entry
之后的行为是什么。小时说MenuItem
添加了遗嘱显示为标题Menu
,因此您只需声明真实MenuItem
对象是否显示带有预期标题的WhenSomeActionIsCalledShouldAddAnEntryContainingTitleToMenu
。
实际上,someAction
测试已经通过其名称公开someAction
实现细节,并且当您更改{的算法时,另一方面将测试与实现someAction
耦合。 {1}}测试将失败。您可以将测试添加为displaysAnTitltedMenuItemInMenuWhenSomeActionIsCalledWithinATitle
。
正如您所看到的,测试将以任何方式耦合到实现。我们唯一能做的就是尽可能将实现的测试耦合为 lower ,这样,我们只需在实现更改时更改一些测试。
如果您发现Menu
很尴尬,可以改用MenuSpy
,然后在MenuSpy
和MenuImpl
内写一些IntegrationContractTest。 MenuSpy
。正如您所看到的那样,使用Menu
的测试与实际的Entry
相比要高一些,因为我们希望在MenuSpy
中添加标题Menu
而不是Entry
标题MenuSpy
添加后Entry
的效果。
让someAction(title);
menuSpy->hasReceivedATitledEntry(title);
检查是否添加了标题Entry
,例如:
CheckableEntry
或:您可能会在标题返回之前检查添加的getAddedEntryTitle
someAction(title);
EXPECT_TRUE(menuSpy->getAddedEntryTitle() == title);
方法中{{1}}是否为{{1}}。
{{1}}