您如何在TDD中组织您的单元测试?

26

我实行TDD,但对于组织单元测试并不是很严格。通常我会用一个文件来代表下一个故事或功能块,并编写所有的单元测试来使其工作。

当然,如果我要引入一个新的类,通常我会为该类创建一个单独的单元测试模块或文件,但我并不将这些测试本身组织成任何更高级别的结构。结果就是我能快速编写代码,并且我相信我的实际程序结构合理,但单元测试本身比较“凌乱”。特别是它们的结构倾向于重复开发过程的进化史。有时候我发现自己在代码中换了懒惰,但在测试中也换了懒惰。

这到底是多大的问题?在这里有谁不断地重构和重新组织他们的单元测试以尝试改善整体结构?有什么建议吗?测试的整体结构应该是什么样的。

(注意,我问的不是这里提出的“每个函数/方法应该写多少个断言”的问题:我应该为每个函数/方法编写多少个单元测试? 我谈论的是更大的局面。)

5个回答

17

将你的测试分成两组:

  • 功能测试
  • 单元测试

功能测试是每个用户故事的测试。单元测试是每个类的测试。前者检查您是否实际支持该故事,后者则对您的功能进行测试和记录。

有一个目录(包)用于功能测试。单元测试应与它们所测试的功能密切相关(因此它们是分散的)。您可以随着代码的移动和重构而移动和重构它们。


你是如何组织单元测试的?我过去通常是为每个方法编写一个测试。但是,假设我有一个“clear()”和一个“isEmpty()”方法。这两个方法是相互关联的。为它们编写两个测试用例并没有太多意义,因为它们实际上是等价的:1. 调用 clear(),然后检查 isEmpty() 是否返回 true。到目前为止,我还没有找到一种组织策略,可以保持清晰并避免冗余。(clear/isEmtpy 看起来很简单,但实际上可能会更加困难) - Silicomancer

4

不太重要的部分是组织测试。

我首先将测试放入与被测试类相关的类中,因此com.jeffreyfredrick.Foo有一个测试com.jeffreyfredrick.FooTest。但是如果这些类的某个子集需要不同的设置,则将它们移动到自己的测试类中。我将我的测试放在单独的源目录中,但仍保留在同一项目中。

更重要的部分是重构测试。

是的,我会尝试在进行测试时进行重构。目标是消除重复,同时仍然保持声明性和易于阅读。这在测试类内部和测试类之间都是正确的。在测试类内部,我可能会有一个参数化方法来创建测试伪造(模拟或存根)。我的测试伪造通常是测试类内部的内部类,但如果我发现有需要,我会将它们拿出来以便在测试中重用。当适当时,我还将创建一个TestUtil类来处理常见方法。

我认为重构您的测试对于长期成功的大型项目单元测试非常重要。您听说过人们如何抱怨他们的测试太脆弱或阻止他们进行更改吗?您不希望处于这样的位置,即更改类的行为意味着对测试进行数十甚至数百次更改。就像编码一样,通过重构和保持测试的清洁来实现这一点。

测试就是代码。


3

对于软件中的每个类,我都会维护一个单元测试类。这些单元测试类遵循与被测试的类相同的包层次结构。

我将我的单元测试代码保存在一个单独的项目中。有些人也喜欢在同一项目中将测试代码保存在名为“test”的单独源目录下。你可以按照自己感觉舒适的方式进行。


3
我为应用程序中的每个类编写一个单元测试类,并将测试类组织在与被测试类相同的包结构中。 在每个测试类中,我没有太多的组织结构。每个测试类只有少量的方法来测试被测试类中的每个公共方法,所以我从来没有遇到过找不到我需要的东西的问题。

你如何保持测试与被测试类在相同的包结构中组织?如果我尝试这样做,我发现我无法跟上被测试类的重构。 - Peter Ajtai
2
@PeterAjtai 我的IDE(Eclipse)部分地为我处理了这个问题。我有一个根源代码文件夹和一个单独的根测试文件夹。每个文件夹内都有相同的包文件夹。当我在Eclipse中右键单击一个类并选择创建新的JUnit测试类的菜单选项时,我只需将测试文件夹选择为根位置,其余的包目录结构就会自动复制。如果在重构过程中方法被拆分到一个新类中,我会以同样的方式创建一个新的测试类,但需要手动移动和编辑旧的测试方法。 - Bill the Lizard

0

我试图将单元测试视为一个独立的项目。与任何项目一样,组织应该遵循一些内部逻辑。但它不必是具体或正式定义的 - 只要您感到舒适,就可以了,只要它能保持您的项目井然有序和清洁。

因此,对于单元测试,我通常要么遵循主项目代码结构,要么(有时当情况需要时)专注于功能区域。

把它们留在一个堆中,就像你想象的那样杂乱无章,难以维护。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接