Unit testing 关于单元测试的几个问题

Unit testing 关于单元测试的几个问题,unit-testing,testing,tdd,test-first,Unit Testing,Testing,Tdd,Test First,假设我们首先设计堆栈类测试(TDD): 然后 Should_Handle_More_Items_Than_The_Default_Internal_Array_Size() 但经过一番思考,我得出了这样的结论:也许下面这样更合适: Should_Double_Its_Size_Every_Time_Its_Full() 在第一种情况下,我的推理必须这样做,我只说它做了什么,而不是什么时候。 在第二个例子中,我说的是什么时候添加更多的项目,但我也说了我是如何(在内部)实现它的,这可能是不正确的

假设我们首先设计堆栈类测试(TDD):

然后

Should_Handle_More_Items_Than_The_Default_Internal_Array_Size()
但经过一番思考,我得出了这样的结论:也许下面这样更合适:

Should_Double_Its_Size_Every_Time_Its_Full()
在第一种情况下,我的推理必须这样做,我只说它做了什么,而不是什么时候。 在第二个例子中,我说的是什么时候添加更多的项目,但我也说了我是如何(在内部)实现它的,这可能是不正确的。在我看来(我不确定我是否正确),我的测试应该考虑SUT与外部的可能交互,而不是内部如何实现。我说得对吗

在我看来,第三个选项是最好的,因为它清楚地说明了它的功能(大小增加——事实上,是它的两倍),当它完成时(当它已满),并且不会将我与任何特定的实现联系在一起(我可能以后想将它更改为内部ArrayList!)


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

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


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

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

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

在我看来(我不确定我是不是 正确),我的测试应该是可能的 我的SUT与 外观,而不是它是如何 内部实施。我说得对吗

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

请记住,在设计类时,您不希望公开任何指示其实现方式的内容。该类的公共成员指示如何与它交互,现在指示它在幕后如何工作

在我看来(我不确定我是不是 正确),我的测试应该是可能的 我的SUT与 外观,而不是它是如何 内部实施。我说得对吗

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


请记住,在设计类时,您不希望公开任何指示其实现方式的内容。该类的公共成员指明了如何与它交互,以及它在幕后的工作方式。

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

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

问问你自己,你愿意为这个类承诺什么

您或该类用户是否真正关心容量是增加一倍、增加一倍还是增加一千倍?如果是这样,您应该修改界面,以便他们可以指定用于增加容量的策略:

public Stack<T>(CapacityGrowthStyle capacityGrowthStyle) { ... }

我会以同样的方式回答你的第二个问题:如果你的类的用户关心底层数据结构,那么你的测试应该只反映底层数据结构。

问问你自己,你愿意为这个类承诺什么

您或该类用户是否真正关心容量是增加一倍、增加一倍还是增加一千倍?如果是这样,您应该修改界面,以便他们可以指定用于增加容量的策略:

public Stack<T>(CapacityGrowthStyle capacityGrowthStyle) { ... }

我将以同样的方式回答您的第二个问题:如果您的类的用户关心底层数据结构,那么您的测试应该只反映底层数据结构。

在我看来,您的测试不应该对内部工作进行假设。如果以后您选择更改一些内部实现细节,您就不想重写所有的测试。在我看来,您的测试不应该对内部工作进行假设。如果以后您选择更改一些内部实现细节,您就不想重写所有的测试。当执行此类TDD样式时,我将执行什么样的测试,然后?测试外部接口并确保其按预期工作。当执行此类TDD样式时,我将执行什么样的测试,然后?测试外部接口,确保它按预期工作。我明白你的观点。现在,如果我遵循TDD指南,我应该怎么做才能添加这个“功能点”?现在我明白了,这是我的主要问题@吞食-这是一个有趣(和困难)的问题。如果容量增长算法对我的用户真的很重要(大概是出于性能原因),我会尝试添加一个
容量
属性以方便测试,即使是
public Stack<T>(CapacityGrowthStyle capacityGrowthStyle) { ... }
[Test]
public void Can_Handle_X_Items() { ... }

[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void Cannot_Handle_More_Than_X_Items() { ... }