让我们假设我们正在设计一个Stack类测试优先(TDD):
public class Stack<T> {
private T[] elements = new T[16];
private int size = 0;
...
}
此Stack使用16号内部数组来存储其元素。它将正常工作,直到您需要添加第17个元素。因为我可能需要第17个元素,所以我决定将这个功能添加到我的Stack中,所以我开始考虑我可以为测试提供什么名称,以便添加该功能。这将是我第一个问题的主题。
我首先选择了以下形式的东西:
Should_Be_Able_To_Correctly_Increase_Its_Inner_Array_Size()
然后
Should_Handle_More_Items_Than_The_Default_Internal_Array_Size()
但是在考虑了一下后,我得出的结论是,以下内容可能会更合适:
Should_Double_Its_Size_Every_Time_Its_Full()
我的推理在第一种情况下必须这样做,我只说它的作用,但不是在什么时候。 在第二个,我说什么时候添加更多的项目,但我也在说明我是如何(内部)实现它的,这可能不正确。在我看来(我不确定我是否正确),我的测试应该是我的SUT与外部的可能交互,而不是内部如何实现。我是对的吗?
在我看来,第三种选择是最好的,因为它清楚地说明它的作用(增大尺寸 - 实际上是它的尺寸加倍),当它完成时(当它充满时)并且不会束缚我任何具体的实现(我以后可能想把它改成一个内部的ArrayList!)。
这引出了我的第二个问题:假设我为我的Stack类进行了所有单元测试,内部使用了一个数组并且它正常工作并且正如预期的那样,如果我的测试保持不变以后想要重构并将Array更改为ArrayList或任何其他类型的数据结构?或者测试以任何方式反映出来?我猜不是,但我不确定。
答案 0 :(得分:3)
在我看来(我不确定我是谁 正确),我的测试应该是可能的 我的SUT与...的互动 外观,而不是如何 内部实施。我是对的吗?
你是对的。如果更改类的内部实现,则单元测试应保持不变。如果您在外部公开任何新内容,则应创建新的单元测试以考虑这些更改。
请记住,当您设计课程时,您不希望暴露任何指示其实施方式的内容。该班级的公共成员表明如何与之互动,现在它是如何在幕后工作的。
答案 1 :(得分:2)
我不确定如何通过单元测试来测试内部列表或数组大小。您无法通过Stack接口访问它。单元测试用于测试外部合同。如果您想测试实现细节,请尝试其他方法。
你的第二个问题的答案是肯定的,如果是单位测试,测试仍然应该通过。
答案 2 :(得分:2)
问问自己,你愿意为这堂课做些什么。
您或班级的消费者是否真正关心容量是否增加一倍,增加一倍或增加一千?如果是这样,您应该修改界面,以便他们可以指定用于增加容量的策略:
public Stack<T>(CapacityGrowthStyle capacityGrowthStyle) { ... }
如果没有,只需编写测试来记录容量并将其留在那里(下面X
是您的基础数据结构的限制):
[Test]
public void Can_Handle_X_Items() { ... }
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void Cannot_Handle_More_Than_X_Items() { ... }
我会同样回答你的第二个问题:你的测试应该只反映基础数据结构,如果你的班级用户会关心它。
答案 3 :(得分:1)
我不确定您是否应通过单元测试来测试内部列表或数组大小,因为您无法通过Stack接口访问它。有很多方法可以实现堆栈,有些是好的,有些是坏的,但正如Bernard所说,那些是内部实现。单元测试旨在测试面向外的功能。