我正在阅读代码完成。在那本书中,Steve McConnell 警告说“开发人员测试往往是‘干净的测试’。开发人员倾向于测试代码是否有效(干净测试),而不是测试代码破坏的所有方式(脏测试)。”
如何为代码中断的方式编写测试?我的意思是,我可以为错误的输入编写测试,并确保它被正确阻止。但除此之外,我应该考虑什么样的事情?麦康奈尔在这里是什么意思?我对基本的单元测试很满意,但我正在努力掌握它。
我正在阅读代码完成。在那本书中,Steve McConnell 警告说“开发人员测试往往是‘干净的测试’。开发人员倾向于测试代码是否有效(干净测试),而不是测试代码破坏的所有方式(脏测试)。”
如何为代码中断的方式编写测试?我的意思是,我可以为错误的输入编写测试,并确保它被正确阻止。但除此之外,我应该考虑什么样的事情?麦康奈尔在这里是什么意思?我对基本的单元测试很满意,但我正在努力掌握它。
我认为你在这里是正确的道路。证明代码有效的测试将调用具有合理、有意义和预期输入的方法,同时程序处于正常状态。虽然破坏代码的测试试图“开箱即用”地思考那段代码,但因此使用任何类型的无意义或意外输入。
恕我直言,重要的是要了解这两个思维过程是非常不同的。当开发人员以 TDD 方式编写代码时,他倾向于关注要在代码中实现的各种功能,以及用于证明该功能或用例按规定工作的测试。以这种方式创建的测试就是 McConnell 所说的“干净测试”。
考虑如何破解一段代码需要一个非常不同的思维过程和不同的经验。它需要从不同的角度看待您的方法和 API,例如暂时搁置您对这些方法和参数的目的的了解,只关注技术上可以用它们做的事情。还要考虑该方法正常工作所需的所有(通常是隐含的)先决条件或依赖关系。它是否取决于从数据库读取的配置参数?它会写入文件系统吗?它是否调用了另一个组件,并期望它事先已正确初始化?它是否使用大量内存?它是否在 GUI 上显示消息?...如果其中一个或多个不成立怎么办?
所有这些都引出了重要的问题:你的方法应该如何处理这种肮脏的情况?它应该崩溃吗?抛出异常?尽可能地继续?返回错误代码?记录错误报告?...所有这些小的或更大的决策实际上对于正确且一致地定义方法或 API 的合同非常重要。
Kent Beck 谈到在“开发者帽”和“测试者帽”之间切换的意义相同。以这种方式流畅地转换观点和思维过程需要实践和经验。
作者最可能所说的干净测试是指仅验证方法执行的快乐路径的测试。
测试快乐路径通常是最简单的,人们可能倾向于认为既然它有效,他们编写测试的工作就完成了。这种情况很少见。考虑:
public void SaveLog(string entry)
{
var outputFile = this.outputFileProvider.GetLogFile();
var isValid = this.logValidator.IsValid(outputFile);
if (isValid)
{
this.logWriter.Write(outputFile, entry);
}
}
快乐的路径测试将简单地假设所有依赖项(outputFileProvider
, logValidator
, logWriter
)都工作并且确实发生了写入。但是,在此过程中很可能会出现问题,并且这些路径也应该进行测试。喜欢:
outputFileProvider
无法获取输出文件outputFile
原来是无效的logValidator
失败并有自己的例外logWriter
写不出来仅举几个!单元测试比简单地检查快乐路径要多得多,但不幸的是,这种情况经常发生。
Test Obsessed的 Elisabeth Hendrickson有一份测试启发式备忘单,其中列出了各种测试方法。该文档广泛涉及测试,但名为“数据类型攻击”的部分有许多“不愉快路径”单元测试可以查看的具体示例。
例如,以下是她关于测试路径和文件的方法的想法:
长名称 (>255 个字符)
名称中的特殊字符 (空格 * ? / \ | < > , . ( ) [ ] { } ; : ' “ !@ # $ % ^ &)
不存在
已经存在
没有空格
最小空格
写入-Protected
Unavailable
锁定
在远程机器
损坏