1

假设我们正在设计一个 Stack 类 test-first (TDD):

public class Stack<T> {
    private T[] elements = new T[16];
    private int size = 0;
    ...
}

该堆栈使用大小为 16 的内部数组来存储其元素。在您需要添加第 17 个元素之前,它会正常工作。因为我可能需要第 17 个元素,所以我决定将这个功能添加到我的堆栈中,所以我开始考虑我可以为必须添加该功能的测试起什么名字。这将是我第一个问题的主题。

我首先选择了以下形式的东西:

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 与外部的可能交互,而不是它如何在内部实现。我对吗?

在我看来,第 3 个选项是最好的,因为它清楚地说明了它的作用(大小增加——实际上,它的大小翻了一番),什么时候做(当它满了),并且没有将我与任何特定的实现(我可能稍后想将其更改为内部 ArrayList!)。

这引出了我的第二个问题:假设我为内部使用数组的 Stack 类进行了所有单元测试,并且它工作正常并且如预期的那样,如果我以后想要重构并将数组更改为ArrayList 或任何其他类型的数据结构?或者测试是否应该以任何方式反映这一点?我猜没有,但我不确定。

4

4 回答 4

3

在我看来(我不确定我是否正确),我的测试应该是我的 SUT 与外部的可能交互,而不是它如何在内部实现。我对吗?

你是对的。如果您更改类的内部实现,则单元测试应该保持不变。如果你在外部公开任何新的东西,你应该创建新的单元测试来解释这些变化。

请记住,当你设计你的类时,你不想暴露任何表明它是如何实现的。该类的公共成员指示如何与之交互,现在它是如何在幕后工作的。

于 2010-08-30T19:11:36.357 回答
2

我不确定如何通过单元测试来测试内部列表或数组大小。您无法通过 Stack 接口访问它。单元测试用于测试外部合约。如果您想测试实现细节,请尝试其他方法。

你的第二个问题的答案是肯定的,如果是单元测试,测试应该仍然通过。

于 2010-08-30T19:09:33.953 回答
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() { ... }

我会类似地回答你的第二个问题:如果你的类的用户会关心它,你的测试应该只反映底层数据结构。

于 2010-08-30T19:27:55.890 回答
1

我不确定您是否应该通过单元测试来测试内部列表或数组大小,因为您无法通过 Stack 接口访问它。实现堆栈的方法有很多,有好有坏,但正如 Bernard 所说,这些都是内部实现。单元测试旨在测试面向外部的功能。

于 2010-08-30T19:26:13.747 回答