单元和单元集成测试的检查清单






3.69/5 (7投票s)
在进行或组织单元和单元集成测试时值得检查的事项。
引言
随着时间的推移,我整理了一份关于单元测试和单元集成测试的清单;包括书籍、文章、论坛和博客。我并没有始终如一地记录信息来源。因此,本文底部的参考文献并不完整。
最近我发现了 Gerard Meszaros 的 XUnit Test Patterns 网站(真希望我早点发现它)。尽管如此,我还是想与您分享以下列表。这或许可以帮助您验证在组织和/或进行单元(集成)测试方面的努力。也许您有可以添加的要点?或者您不同意?无论如何,我都希望听到您的意见。
背景
单元测试是测试程序中单个子程序(类)、子例程或过程(方法)的过程。大多数情况下,这些“单元”与其他模块紧密耦合。有时,后者会被存根(stubbed out)以确保我们只测试待测单元,而不是二次单元。单元集成测试涉及开发人员所创建的各个单元之间的交互。(小范围集成。)
注意事项
- 您知道如何将单元测试纳入开发过程吗?
- 单元测试由编写代码的开发人员编写,并由开发人员执行。
- 编码和发现错误之间的反馈周期更短。
- 编码员和测试员是同一个人。
- 计入开发预算。
- 持续测试与阶段末测试。
- 避免出现“没时间进行单元测试,我必须尽快交付”之类的争论。
- 不要依赖阶段末测试来查找错误。
- 您是否有策略将单元测试代码整合到生产代码组织中?
- 要准备好看到与生产代码一样多的测试代码,因此良好的组织至关重要。
- 需要集成到源代码控制系统中。
- 您是否了解单元测试旨在查找的错误类型?
- 有些错误比在正式的阶段末测试中更容易通过单元(集成)测试发现。或者至少,要发现这些错误需要付出更大的努力。
- 您是否使用自动化工具,如 NUnit 或 VS2005 集成测试框架,来简化单元测试过程并使其可重复?
- 所有开发人员都可以使用相同的框架。所有开发人员都可以使用相同的框架。自动化有两个方面:调用测试和检查结果。
- 可以集成到自动构建/持续集成方案中。
- 您知道哪种类型的代码(层)最受益于单元测试吗?
- 某些类型的代码比其他类型的代码更适合单元测试,例如业务逻辑与用户界面。
- 每当您想写类似 `debug.writeLine` 的东西时,就为它写一个测试。
- 您知道如何处理难以进行单元测试的代码吗?
- 考虑其他测试方法
- GUI 皮肤代码、主方法、异步方法、多线程
- 您知道如何处理不编写单元代码的开发人员吗?
- 教育程度
- 通过展示优势来消除阻力
- 您知道如何避免在临近截止日期时单元测试被推迟吗?
- 纳入计划(额外 10%-30%?)
- 您是否有正确的观念来执行单元测试?
- 展示其有效性 vs. 发现错误 vs. 验证期望(规范)与实现(代码)的对比。
- 您是否准备好仔细审查自己的工作?
- 您知道何时编写单元测试吗?
- 边写代码边编写测试。
- 不要留待“稍后”,这可能永远不会到来。
- 您知道何时执行单元测试吗?
- 尽可能频繁地运行单元测试,以缩短编码和发现错误之间的反馈周期。
- 您知道如何将单元测试集成到构建过程/持续集成方案中吗?
- 无人值守执行单元测试,以便尽可能多地运行测试。
- 您是否有关于如何处理单元测试失败的策略?
- 在将代码签入源代码控制系统之前,请确保代码通过了单元测试和集成测试。
- 您是否区分了纯单元测试和单元集成测试,以及何时使用它们?
- 为函数中的协作单元编写测试,而不是采用系统测试中的大爆炸方法。
- 单元测试非常适合开发过程中的回归测试。
- 您是否有关于分发单元测试和单元(集成)测试以及自动化测试通用知识的政策?
- 优化资源利用
- 您是否有关于使用单元测试进行(正式)回归测试的政策?
- 对现有产品进行的更改可能不会导致单元测试失败。
- 您是否有关于保持单元测试质量的政策?
- 避免由于时间压力导致最初的单元测试良好,而后续单元测试质量(覆盖率)较低。
- 您是否认为单元测试代码与生产代码处于同一水平?
- 准备好看到与测试代码一样多的生产代码。
- 测试必须以与生产代码相同的专业标准编写和维护。
- 您是否对接口而不是类进行编码?
- 以便在运行时或测试时插入不同的实现。
- 可以轻松提供不需要复杂实现的测试存根,因为它们不需要依赖支持基础设施。
- 您是否使用控制反转/依赖注入模式?
- 这使得向被测试类提供协作类的替代实现更加容易。
- 每个类是否都有明确定义的职责集?
- 这样更容易编写单元测试。高内聚,低耦合。
- 您是否为了可测试性而暴露了类过多?
- 不要为了可测试性而将方法标记为 `public`。
- 牢记封装。VS2005 测试框架可以处理标记为 `friend` 或 `private` 的类和方法。
- 您是否将单元测试代码与被测试代码分开?
- 不必要的部署代码。
- 每个被测试类是否都有一个测试类?
- VS2005 测试框架的代码生成鼓励这样做,并在您生成其他测试方法骨架时使用它。
- 每个被测试程序集是否都有一个独立的程序集?
- VS2005 测试框架的代码生成鼓励这样做,并在您生成其他测试类骨架时使用它。
- 是否区分了需要配置文件和不需要配置文件的测试?
- 这样可以更容易地确定哪些测试应该可以在任何机器上运行,而无需进行设置(例如,构建服务器)。
- 由于 xUnit 框架的工作方式,请注意测试数据/配置文件放置的位置。
- 是否区分了纯单元测试和单元集成测试?
- 在使用单元集成测试时,您必须始终验证协作单元是否在测试应该失败时引起了错误。
- 您是否将数据库访问代码测试分开?
- 这些类型的测试在准备、配置和执行速度方面需要更多关注。
- 单元测试代码是否在源代码控制之下?它是否是代码库的一部分。
- 任何开发人员之后都必须能够使用它。
- 自动系统(构建/CI)必须能够使用它。
- 所有测试方法是否都能独立运行,因此它们不依赖于其他测试,也不需要按特定顺序运行?
- 可以通过测试 UI 选择/取消选择测试方法,因此测试必须相互独立运行。
- VS2005 测试框架允许按顺序运行测试(更偏向功能性测试,用例脚本)。
- 每个测试是否都能一遍又一遍地运行,以任何顺序运行,并产生相同的结果?
- 否则断言的自动化将很困难。
- 您是否使用模拟对象或存根来实现测试的可重复性?
- 使用模拟对象/存根可以帮助您隔离被测对象,并使其独立于环境,因为它们总是提供预期的值。
- 您是否考虑了正向单元测试?
- 以预期的方式执行代码并验证正确的结果。
- 您是否考虑了负向单元测试?
- 故意滥用代码并验证健壮性和适当的错误处理。
- 您是否考虑了“压力”测试?
- 检查代码的极限(溢出等)。
- 您是否考虑了数据一致性测试?
- 检查结果值是否符合预期的格式。
- 检查代码如何处理符合预期数据格式的输入值,反之亦然。
- 您是否考虑了排序测试?
- 检查结果值集是否按顺序或无序排列(如适用)。
- 检查代码如何处理有序或无序的输入值。
- 您是否考虑了范围测试?
- 检查结果是否在合理的最小值和最大值范围内。
- 检查代码如何处理在合理最小值和最大值范围内的输入值。
- 检查代码如何处理超出合理最小值和最大值边界的输入值。
- 您是否考虑了参考测试?
- 在方法结束时,后置条件是您保证方法将实现的事项。
- 方法返回的直接结果是显而易见的事项,但如果方法有任何副作用,则需要一并检查。
- 您是否考虑了存在性测试?
- 测试结果是否存在(例如,非 null、非零、存在于集合中等)?
- 测试代码如何处理空值或缺失的输入值(如 0、0.0、“”或 null)。
- 您是否考虑了基数测试?
- 测试结果中是否有恰好足够的值。
- 查看代码如何处理大小正确或包含重复项的输入值列表。
- 您是否考虑了过程逻辑测试?
- 检查所有控制结构构造(`if`、`else`、`case` 等)是否正常工作。您是否考虑了哪些代码部分适合测试无效参数?
- 检查系统边界处的输入,您就不必在系统内部重复这些测试。内部组件可以信任,如果数据已经进入系统,那么它一定是没问题的。您仍然可以采取悲观的方式,但要准备看到大量额外的单元测试。
- 您使用代码覆盖率工具吗?
- 以验证单元测试的彻底性。
- 您为 bug 修复编写测试吗?
- 以证明 bug 已不再存在。
- 您是否测试特定的数字/日期时间问题,如舍入、区域设置?
- 小数点符号,DD/MM/YYY 与 MM/DD/YYYY。
- 单元测试方法是否只测试一项特定内容?
- 当单元测试失败时,您可以更容易地精确定位问题。甚至可能无需调试。
- 在进行单元集成测试时,您是否考虑使用模拟库或存根?
- 存根是一段代码,用于替代其他编程功能。存根可以模拟现有代码的行为(如远程机器上的过程),或作为尚未开发的代码的临时替代品。因此,存根在单元测试中很有用。
- 模拟对象是模拟对象,它们以受控方式模仿真实对象的行为。在单元测试中,模拟对象可以模拟复杂、真实(非模拟)对象的行为,因此在单元测试中难以或不可能纳入真实对象时很有用。
- 测试套件是否足够自文档化?
- 有意义的测试用例名称和断言消息形式的文档通常比对测试用例本身的注释更重要。
- 当测试失败时,输出将显示失败的断言(或错误)以及相关测试的名称。
- 测试方法的名称是否能清晰地表达意图?
- 单元测试名称会显示在交互式屏幕报告和控制台输出中。
- 您是否避免在测试方法名称后附加数字来区分它们?
- 这不能很好地传达您的意图。
- 单元测试代码是否有统一的命名约定?
- 这样您就知道哪些是测试方法,哪些是辅助方法。
- 您是否考虑将每个测试方法中使用的设置代码放入 XUnit 框架的单独初始化方法中,这些方法会在类级别或方法级别触发?
- 过多的数据设置会使单元测试变得难以理解,因此如有必要请隐藏它。
- 所有状态都是预先知道的,并且无论您的测试在哪里或何时运行,都将保持不变。
- 避免一个单元测试留下一些“脏数据”残留。
- 在运行被测方法时必须为真的前提条件。
- 您的单元测试是否快速?
- 许多单元测试会经常启动,因此快速执行可以避免摩擦,并鼓励您频繁运行它们。
- 您是否考虑了工厂方法来为您创建被测对象实例?
- 然后,您可以在其他测试方法中重复使用该方法来获取被测类的最新实例。
- 这有助于保持测试的可维护性,并保护您的测试免受被测代码意外更改的影响。
- 被测方法的实际执行与断言结果的执行是否分开,将结果存储在另一个变量中?
- 对被测对象的调用可能很长,并且可能导致您的 Assert 行超出屏幕边缘,迫使测试读者向右滚动。
- 包含结果的变量名称是否可读?
- 使您的 Assert 行非常易于理解和阅读。
- 您是否考虑使用单独的验证方法?这是测试类中可重用的方法,其中包含 Assert 语句,但可以接受不同的输入并验证它们。当您对不同的输入断言相同的内容时,可以使用这些验证方法。即使 Assert 位于不同的方法中,如果 Assert 失败,您仍然会收到断言异常,并且原始调用测试将显示在测试失败输出窗口中。可以提高可读性。
- 您是否避免在一个单元测试中使用多个 Assert?
- 失败后,后续的 Assert 不会执行。
- 这些未使用的 Assert 可以提供有价值的数据(或症状),帮助您快速缩小关注范围并发现根本问题。
- 将需要运行的附加 Assert 放在单独的、自包含的单元测试中,以便您有机会看到什么失败了。
- 您是否使用了信息丰富的断言消息?
- 一个好的 Assert 消息应该解释应该发生什么,或者发生了什么以及为什么它是错误的。显示在交互式屏幕报告和控制台输出中。测试代码维护。
- 您是否避免了代码重复?
- 根据需要重构测试套件,并识别可以重用代码以加快单个测试用例编写速度的区域。
参考与推荐阅读
- xUnit Test Patterns: Refactoring Test Code, Gerard Meszaros, 2007 by Addison Wesley Professional
- The Art of Unit Testing, Roy Osherove, Manning 2008 (MEAP 2007)
- Next Generation Java Testing: TestNG and Advanced Concepts, Cédric Beust and Hani Suleiman, Addison-Wesley 2007
- Pragmatic Unit Testing in Java with JUnit, Andy Hunt and Dave Thomas, Pragmatic Programmers LLC 2004
- Pragmatic Unit Testing in C# with NUnit: The Pragmatic Starter Kit, Volume II, Andy Hunt and Dave Thomas, Pragmatic Programmers LLC © 2004
- Coder to Developer: Tools and Strategies for Delivering Your Software, Mike Gunderloy, Sybex 2004
- EJB Design Patterns, Floyd Marinescu John Wiley & Sons 2002
- Code Craft: The Practice of Writing Excellent Code, Pete Goodliffe No Starch Press 2007
- Code Complete, Second Edition, Steve McConnell, Microsoft Press 2004