我正在尝试了解有关JUnit和TDD的更多信息,但我遇到了测试用例之间耦合的一些问题。
当我为特定数据类型的API编写测试用例时,比如Deque<T>
,我如何限制测试用例之间的耦合?例如,如果我的方法写入一个测试用例insertFirst(T item)
,似乎简单的假设,我应该能够调用的方法的正确初始化对象上之后,触发两件事情:
Deque
对象的大小应该增加一个T removeFirst()
方法,它应该返回对我通过初始调用插入的对象的引用。 但是,这会在我的至少两个测试用例之间产生不希望的耦合,其中一个测试用例传递依赖于另一个API方法的正确实现。例如,为了使这个测试用例通过,我需要一个正确的实现来检查Deque
中的项目数以及删除项目。如果我对这些方法中的任何一个的测试由于某种原因不正确或不完整,那么我对insertFirst
方法的测试将自动被怀疑。
避免这种情况的最佳做法是什么?我的方法是否以某种方式编写测试用例错误?
答案 0 :(得分:12)
为一个方法编写测试时, 认为其余的类正常工作。如果你不做这个假设,唯一的结论就是每个类的单个大量测试。这不是我们所做的。
您可以假设班级的其他部分正常工作,因为也会对其他部分进行测试,以确保其正确性。
如果一个部件工作不正常,测试将失败,表明某些事情不正确
一旦测试套件测试失败,就必须修复错误。你不再做任何假设。
示例:
你有一个简单的列表实现,只有三种方法:
你有三个测试:
insert
:insert
item( Act )count
是否等于1(断言)remove
:
insert
项目(安排)remove
item( Act )count
是否等于0(断言)count
:
insert
n项(安排)count
(行动)count
是否等于n(断言)现在,如果上述任何测试失败,您无法确定班级中单个成员的正确性:
remove
,因为没有什么可以删除。insert
和count
是否正常工作,因为如果三个成员中的任何一个无法正常工作,第二个测试将失败。失败的测试告诉你一些事情:
根据失败的测试,您通常可以减去错误的位置
示例:如果只有第二个测试失败但不是第一个或第三个测试失败,则错误很可能是remove
方法。
答案 1 :(得分:4)
将单元测试视为测试特定功能而非特定方法通常会更高效。任何给定的测试都将检查某些方法集合是否正常工作以实现作为测试主题的特性,并且精心设计的测试集中的失败模式将倾向于告诉您哪种方法很快就会破坏。
好的测试集合往往会自然而然地脱离TDD;这是使技术如此强大的事情之一。如果我正在编写Deque
,我写的测试往往是以下内容,通常按此顺序显示。
empty_Deque_isEmpty
- 实施isEmpty
以始终返回true
non_empty_Deque_isntEmpty
- 实施insertFirst
以使isEmpty
实例变量为false re_emptied_Deque_isEmpty
- 将isEmpty
使用的实例变量更改为响应insertFirst
和removeFirst
is_empty_Deque_size_correct
- 实施size
始终返回0 is_nonempty_Deque_size_correct
- 将实例变量添加到曲目大小;意识到它正在做isEmpty
所需的同样的事情;重构is_re_emptied_Deque_size_correct
- 由于我们所做的事情而使测试刚刚过去了5.发生does_removing_from_empty_Deque_throw
- removeFirst
需要在执行任何其他操作之前检查size
is_inserted_item_returned
- insertFirst
和removeFirst
现在填充T
个实例变量is_inserted_item_returned_from_end
- 添加removeLast
的副本removeFirst
;重构is_rear_inserted_item_returned
- 添加复制insertLast
的{{1}};重构insertFirst
- 更改are_all_inserted_items_returned
和insertFirst
以对removeFirst
采取行动;指出不检查检索顺序SomeKindOfCollection<T>
- 插入两件事,确保does_removeFirst_retrieve_items_in_correct_order
返回第二件事。可能已经成真。removeFirst
- 与does_removeLast_retrieve_items_in_correct_order
同上,但非常确定不会通过。这是一大堆测试,但是当你仔细研究它们时,你应该注意到这种模式。这些测试都不是“removeLast
”的测试或“count
的测试”。但是到了我们的时候,整个类的接口都在运行,并且开发了该接口所需的所有内部构件。一些测试依赖于多种方法,如果该方法失败,它们都会破坏。但是,断点模式在确定错误的位置方面往往非常有用。
同样有趣的是我们可以通过多少这些测试来传递,而不需要提交实际在对象中有一个集合,这表明这组测试可以被分解为更通用的测试套件,这将是有用的在开发removeFirst
。