写点什么

一种有效的测试策略

  • 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:036220

评论

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

「 人气作者 TOP10 公布 」—— InfoQ 写作平台【 1 周年盛典 】

InfoQ写作社区官方

1 周年盛典 热门活动

更揽群碑荟一堂|靠谱点评

无量靠谱

TimSort — 以人命名的排序法

D

数据中台在企业数字化转型中的践行(上篇)

EAWorld

架构训练营作业-模块二

西伯利亚鼯鼠

StreamX: 流批一体大数据平台正式开源

ApacheStreamPark

情指勤指挥调度平台搭建,公安重点人员管控平台搭建

如何横扫大厂Offer?这份“1000道Java后端面试速成笔记”程序员(银四)必备

比伯

Java 程序员 程序人生 软件架构 java编程

Redis-Sentinel 深入浅出原理和实战

leonsh

redis redis sentinel

手机自带的多功能百宝箱,没人用真的太可惜了。

彭宏豪95

iphone 效率 工具 苹果 4月日更

重读《重构2》- 提取函数

顿晓

4月日更 提取函数

好运转购物商城

飞亚科技

身份和访问管理标准及挑战

龙归科技

物联网 云服务 认证授权 身份和访问管理

Vue cli项目 修改运行命令和端口号

空城机

Vue 大前端 4月日更 vue cli

年度盛会 | Atlassian Team 2021 全球大会重磅嘉宾介绍

Atlassian

Agile Zoom Atlassian Slack

深度图解Redis Cluster原理

leonsh

redis Gossip redis cluster

逐行解读Spring- 没人比我更懂循环依赖

学Java关注我

Java 编程 架构 编程语言 技术宅

融云音视频产品免费 20 万分钟 助力通信场景全搭建

融云 RongCloud

OSI七层模型

赖猫

TCP 后台开发 后端 网络编程 网络协议

模块 1 作业

流水亮

Java入门第一课

ベ布小禅

4月日更

快速定制OCR识别模型好帮手:百度EasyDL OCR自训练平台上线啦!

百度大脑

EasyDL-OCR

Rust:关于闭包的一点研究

Microwood

rust 闭包 closure

二阶单位圆

山@支

字节三面:对于Spring你了解多少?如果你会了这150道题 ,吊打面试官岂不是洒洒水?

北游学Java

Java spring Spring Framework spring Boot Starter spring cloud alibaba

数据中台在企业数字化转型中的践行(下篇)

EAWorld

想靠接私活月入5W?建议根据这几个开源的SpringBoot项目(含小程序)模仿一下~

Java架构师迁哥

实时 OLAP, 从 0 到 1

Apache Flink

flink OLAP 区块链技术

课程完结有感

Kalman

产品经理 产品经理训练营

Nginx源码编译安装与运行

马里奥

nginx 安装 笔记

绝了!阿里面试官跟我死磕MySQL,结果我却拿了个高薪offer

码农之家

Java 程序员 面试 MySQL 高可用 刷题笔记

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