编写单元测试有如下好处:
利于回归测试
提供文档
改进代码设计
但是,难以阅读和维护的测试代码则会适得其反。本文会提供一些编写单元测试的最佳实践以使得你的测试代码易于维护和理解。
为什么要写单元测试? 1. 花更少的时间进行功能测试功能测试成本相对较高,因为经常需要打开应用并执行一系列操作以验证结果是否符合预期。测试步骤所涉及领域未必是测试人员所熟知,导致需要其他人协助进行测试。对于细微变化,测试可能需几秒钟,亦或几分钟来测试较大的变更。最后,对于系统中的每处修改都需要进行重复测试。
反观单元测试,仅需毫秒级别且无需对系统自身了解过多。单元测试通过与否取决于测试运行器(test runner),而不是某个人。
2. 避免回归测试回归缺陷是在对应用程序进行更改时引入的缺陷。测试人员不仅要测试他们的新特性,还要测试以前存在的特性,以验证之前实现的特性是否仍然像预期的那样运行。
通过单元测试,可以在每次构建之后,即便是只修改了一行代码,重新运行整个测试流程,以确保新代码不会破坏已有功能。
3. 可执行的文档有时对于特定的参数,方法的预期输出难以确定。你或许会问,如果向方法中传入空字符串或者null会发生什么?
当编写具有良好命名的测试用例时,每个用例可以清晰的说明对于给定的输入会有怎样的输出。此外,测试用例还应可以验证方法是否能够正常工作。
4. 低耦合代码编写单元测试可以降低代码耦合度,因为高耦合的代码将会使得单元测试变得困难重重。
良好的单元测试应具备以下特征
快速
对于大型成熟项目可能会有数千个测试用例。每个测试用例应尽可能快的运行,最好在毫秒级别。
隔离
单元测试是独立的,可以单独运行而不依赖外部元素,如文件系统或数据库。
可重复
在不改变输入的情况下,单元测试的输出结果应保持不变。
自检查
单元测试应自动检测测试是否通过而无需人工干预。
耗时少
如果测试代码所花费的时间远超编写代码的时间,应当考虑重构代码以便于更好测试。即,确保编写测试所花费的
测试用例命名应包含以下几部分:
待测试方法的名称
测试场景
预期结果
为什么这么做
良好的命名可以表达测试意图 。测试不仅仅是用来检测代码是否可以正常工作,还可以提供方法的文档说明。仅仅看一组测试用例,你应该可以推断出代码的行为而无需查看代码。此外,当测试失败时,应该可以清楚的知道哪些场景不符合预期。
Bad:
[Fact] public void Test_Single() { var stringCalculator = new StringCalculator(); var actual = stringCalculator.Add("0"); Assert.Equal(0, actual); }