9 月 13 日,2025 Inclusion・外滩大会「开源嘉年华」正在限量报名中! 了解详情
写点什么

一种有效的测试策略

  • 2014-05-27
  • 本文字数:3257 字

    阅读完需:约 11 分钟

在最近的一个大型项目中,我们在早期就定下了一个目标:不会在软件中使用大量 QA 人员专注于手工测试。通过手工测试发现 bug 极其耗时且成本高昂,这促使团队尝试尽可能的将质量内嵌到产品内部。但这并不意味着手工测试毫无价值,因为人们总能在怎样使用软件上给你一些特别的惊喜。

这是一个为期 18 个月左右,周期很长的项目,并且后续也会持续更新。 在项目初期,团队就意识到项目成功的重中之重在于一个优秀的测试策略,尤其是让我们的团队能够做到:1) 随着项目时间的推移能够持续的提高团队的工作效率。2) 不管面对的变更是大是小都能够具有足够的信心。

我们花费了很长时间才确定了一种有效的策略。这在很大程度上是因为我们不得不学习怎样让我们的程序在所有层上都具有可测性。虽然所有的项目团队成员都具有 TDD(测试驱动开发)的经验,但仅仅这样并不足以建立有效的测试策略。

测试分层

给不同的测试分类是一件令人烦恼的事。有功能测试,集成测试,单元测试,验收测试,slow tests,fast tests,UI 测试…等等等等。然后我们发现属于我们的测试主要有三种类型:

  • 系统测试
  • 皮下测试
  • 单元测试

它们之间的区别主要在于被测试内容的范围。系统测试指的是通过应用的外部接口进行运作,无论对象是一个浏览器,文件下拉菜单,队列,window 窗体应用或者其他的什么东西。

皮下测试运行在外部用户接口之下。如果测试的是 Web 应用,皮下测试在我们理解就是指在真实的类
以及环境部署到位的情况下,通过命令处理器进行发送的表单对象。绕过 UI 层逻辑,直接到达 domain 层。发送表单对象,抛出”成功 / 失败”的执行结果。

对于最底层而言,我们有单元测试。单元测试用于测试一个类,并且可以是 fast test 或者 slow test 中的一种。Fast Test 即是常规的 TDD 测试,用于增量的类设计。Test doubles 曾被认为是必要的,但是除非系统交互非常值得关注,否则严格的 基于交互的测试 并不是那么有价值。我们同样也有 slow 单元测试,其同样可被分类为 交互测试。当然它们同样可归类为 repository tests, persistence tests 等。

单元:皮下:系统 测试在我们的测试工作中各自占的比重差不多是 10:2:1。 为了完成项目我们做了大约 5000 个单元测试,1000 个皮下测试,500 个使用 WaitN 以及 Gallio 驱动浏览器的系统测试。6000 个单元 / 皮下测试的执行时间大概是 10 分钟,而剩下的 500 个 UI 测试大概需要 50 分钟完成。

单元测试策略

单元测试是在严密的 TDD 模式下开发的。我们在功能实现之前编写单元测试,并用这些测试驱动代码设计。这些测试可以帮助发现设计问题、封装问题、代码异味等。

我们努力避免编写纯粹用于提供测试的代码。它们常常意味着我们有设计问题、责任错位或封装已被破坏。

随着我们越来越深入项目管道,我们对交互测试的重视越来越少。 如果你真的对交互感兴趣,那么通过 mock 进行的交互测试也仅仅是有趣而已。但更多的时候,我们更感兴趣的是附加作用,而交互只是一个实现细节。反之,我们经常做的是模拟(mock)出过慢或不可测的代码,比如存储库、基于外部服务的外观、配置类等等。否则,我们有限的模拟只是模拟了我们感兴趣的那些观察点。

在大型项目中的某些时间点,为了提取出能加快功能交付的理念,设计往往需要做大型的重构。在我们上一个项目中,我们发掘出了如下理念:

  • AutoMapper
  • 将表单作为单独的命令消息处理
  • Input builders

有了以上这些,单元测试是重构时的保障。但只有我们依赖这些测试来捕获应用程序中所有有趣的行为时,才能有保障的作用。为了允许有效的大中型重构,我们需要增加额外的测试层级。

皮下测试策略

皮下测试,顾名思义,所有的测试都是在用户界面之下进行的。在 MVC 应用程序中,皮下测试是测试控制器下面的所有内容。对于 Web service,一切测试都在终端下进行。皮下测试的思想是,应用程序的最上层不执行任何实际的业务逻辑,而只是外部接口与底层服务之间的连接。

皮下测试的重要性体现在我们希望在抛开如用户接口和外部服务这类外部连接点的情况下,能够在整个系统运行的同时测试业务逻辑。相对于单元测试关注小模块的设计,皮下测试关注的不涉及设计,而是测试整个系统的基本输入和输出。

要建立有效的皮下测试,我们可以尝试通过常见的逻辑流程建立 uniform pinch points。例如,我们可以建立一个命令消息处理系统,或一个普通的查询界面。在最近的一个处理批处理文件项目上,批处理文件中的每一行都被转换为一条消息。然后,我们创造一条消息,发送给这个系统,然后验证处理该消息的所有异常情况。

由于皮下测试不是基于设计而是基于高级(业务)行为,它们是理想的基于场景的测试策略,如 BDD 或 Testcase Class per Fixture 模式。如果我们要进行大的重构,我们需要这些高层次的测试,为商业行为建立全面的安全保障。由于皮下测试更关注于端对端的逻辑,所以它也是标志功能点完成的一个重要的目标点。

虽然皮下测试使我们能够安全地执行较大的重构,但它仍无法保证我们可以放心地将系统升级到生产环境。

全系统测试策略

起初,我们把全系统测试称为“UI 测试”,直到我们的项目越来越多地牵涉到集成策略。这时输入到我们系统中的不再是浏览器,取而代之的是消息,来自 REST 端点、FTP 或批处理文件。UI 测试只是全系统测试的一个子集。全系统测试背后的思想是,我们想按照软件在生产环境中的使用方式来测试它们。对于一个 MVC 应用程序来说,就是基于浏览器的测试。对于批处理文件来说,我们会使用实际的文件。对于 REST 使用实际的 HTTP 请求。对于消息则使用实际的队列和消息。

如果我们想知道,应用程序在部署到生产环境之前是否能按照预期工作,一个有效而且高效的方法是,创建一个自动化测试来测试整个系统。如果我可以让 UI 测试登录到应用程序中、创建一个订单,然后我可以验证是否产生了一个订单请求,那么我会感觉很良好。

关于全系统测试的一个常见误区是认为它就是黑盒测试。然而,全系统测试的特点在于,你必须对系统内部发生的事情了如指掌。实际上,全系统测试甚至还可以利用域模型来生成数据,而不是一个纯粹为测试目的而构建的系统后门。很多团队容易掉进去的一个大坑是,不按照与生产环境一样的代码路径来测试,这将导致系统处于古怪的、无效的、很难处理的状态。

在我们的项目中,全系统测试代码是我们在声称一个功能 / 故事做完之前的所写的最后代码。对于描述一个功能的“已完成”特性来说,手工测试的成本太高、而且不可靠。但是,如果我能像在生产环境一样去测试,通过完全一样的外部界面来完成,那这样的手工测试也算是成功的。

全盘考虑

在一个未经测试过的应用程序中,作为提高覆盖率的手段,我们发现实际上最有价值的测试策略是从全系统测试开始,然后往下移,直至单元测试。我们先做最宽松、最简单的断言,然后慢慢往下移,直至单元级别的逻辑。在全新开发的应用程序中,我们倾向于不只是特别关注某一个区域,因为对于一个需要长期维护的系统来说,所有的测试都很重要。

这种测试策略确实需要一定的投资。我们发现,当我们知道这个应用程序对客户的业务有决定性作用的时候,这样的全盘考虑在特别有效。如果一个应用程序对业务有决定性作用,那它将不得不面临变更。当变更来临的时候,我们最好能安全地实施变更而不影响客户的业务。

1. 作者简介

Jimmy Bogard 是德克萨斯州 Headspring 公司的技术架构师, 致力于研究 DDD, 分部式系统和其他 acronym-centric design/ 架构 / 方法论的研究. 同时也是 AutoMapper 的创始人和 ASP.NET MVC in Action 的作者

原文来自于 LosTechies.com.LosTechies 是一个公开的论坛,致力于通过一种公开且集中的途径分享技术见解和思维。并且希望所有人能够在思维讨论和借鉴中获得成就感和喜悦.

2. 译者简介

Let’s trrrans 翻译小组 (qq 群号:122783748) 一个致力于翻译和分享业界最前沿测试技术以及测试理念的翻译小组


感谢侯伯薇对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-05-27 04:036301

评论

发布
暂无评论
发现更多内容

【线程】,Java自学宝典pdf

Java 程序员 后端

【被面试官吊打】从系统角度考虑性能优化,kafkajvm调优

Java 程序员 后端

【Spring Boot 19】Spring Boot整合阿里云OSS实现云存储

Java 程序员 后端

【数据结构与算法 10】算法的时间复杂度和空间复杂度

Java 程序员 后端

一招搞定 Spring Boot 可视化监控!,java进阶教程云盘

Java 程序员 后端

【Spring Boot 8】Okhttp实现GitHub第三方登录

Java 程序员 后端

【网络信息安全】身份认证,hadoop环境搭建教程

Java 程序员 后端

一文彻底弄懂如何选择抽象类还是接口,linux基础入门知识

Java 程序员 后端

【计算机网络 1】计算机网络概述,Java高级工程师进阶学习—Java热修复原理

Java 程序员 后端

一夜之间火爆GitHub的好文!!阿里资深架构师整理分享

Java 程序员 后端

【并发编程】深入了解volatile,linux高级编程pdf

Java 程序员 后端

一份秀出新天际的SpringCloudAlibaba笔记,把微服务玩的出神入化

Java 程序员 后端

一口气面试6家大厂,已拿5家offer,大厂没有你想象中的难

Java 程序员 后端

【备战秋招冲击大厂】Java面试题系列,你还没弄明白存储键值对

Java 程序员 后端

【大厂面经】我通过了某独角兽公司的魔鬼五面

Java 程序员 后端

【数据库实验】,java语言零基础自学

Java 程序员 后端

【源码分析设计模式 7】Integer中的享元模式

Java 程序员 后端

一文掌握大数据架构师需要具备的能力和格局,别再说你不会JVM性能监控和调优了

Java 程序员 后端

【Spring框架03】DI依赖注入,spring菜鸟教程pdf

Java 程序员 后端

【备战秋招冲击大厂】Java面试题系列(1),springboot入门程序

Java 程序员 后端

【线程】(1),java高级特性编程及实战pdf百度云

Java 程序员 后端

一文带你深扒ClassLoader内核,揭开它的神秘面纱

Java 程序员 后端

【Spring Boot 13】实现热部署,最新Java通用流行框架大全

Java 程序员 后端

【初学入门Demo注解版】SpringBoot ,java面试大全下载

Java 程序员 后端

一场哔哩哔哩Java开发面试之旅,分享面试经历及复习资料

Java 程序员 后端

一文带你了解Java并发中的锁优化,让你的代码运行效率翻倍

Java 程序员 后端

【SpringMVC笔记】Ajax 入门,springboot源码解读与原理分析

Java 程序员 后端

【白话设计模式】去哪儿网一面,java面试题刷题软件

Java 程序员 后端

一年Java开发经验,阿里巴巴五面(已offer,java原理视频

Java 程序员 后端

【关于封装的那些事】 缺失封装,2021年腾讯Java高级面试题及答案

Java 程序员 后端

【嵌入式实验】,面试官必问的技术问题之一

Java 程序员 后端

一种有效的测试策略_语言 & 开发_Jimmy Bogard_InfoQ精选文章