NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

一种有效的测试策略

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

评论

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

影响软件公司开发价格的因素有很多,你知道吗?

开源直播系统源码

软件开发流程 直播系统 app源码 直播源码 定制软件开发

NodeJS 操作cookie 🎀

德育处主任

node.js Node 6月月更

HarmonyOS应用/服务开发流程详解

坚果

6月月更

【Python技能树共建】验证码实操2案例

梦想橡皮擦

6月月更

远程办公带来的挑战和变化 | 社区征文

编程攻略

初夏征文

6月15号MOVE PROTOCOL上线测试版,如何参与呢?

EOSdreamer111

数组(三)

Jason199

数组 js map 6月月更

《知道做到》:如何从知道到做到?

郭明

InfoQ 极客传媒 15 周年庆征文|你真的了解RPC和REST吗?

No Silver Bullet

Rest RPC框架 构架 6月月更 InfoQ极客传媒15周年庆

c语言不挂科(上)

工程师日月

6月月更

滴灌通-服务中小企业的金融创新(36/100)

hackstoic

创业 商业模式

实战 | 【男保姆式】教你打开第一个微信小程序

写程序的小王叔叔

微信开发 小程序开发 6月月更

如何在 Django 中创建应用程序?

海拥(haiyong.site)

django 6月月更

运营商动态路由的神器 :IS-IS 协议

wljslmz

6月月更 路由协议 IS-IS

主数据管理

奔向架构师

数据仓库 主数据 6月月更

如何制定业务的故障分级标准?

华仔

架构实战营 故障定级 故障分级

Linux开发_动态静态库创建与Makefile规则

DS小龙哥

6月月更

详解MOVE PROTOCOL的测试版,让健康运动如影随形

股市老人

Java项目集成activity工作流,快速开发业务审批单据(低代码,敏捷)

金陵老街

敏捷 springboot Activity Vue 3 BPMN

JavaScript期约Promise

大熊G

JavaScript 前端 6月月更

数据库每日一题---第12天:上升的温度

知心宝贝

数据库 云计算 前端 后端 6月月更

互联网公司研发效能/工程效率团队组织架构选择

laofo

互联网 DevOps 研发效能 持续交付 快手

Jetty 运行的时候出现 503 异常的原因

HoneyMoose

C#入门系列(十五) -- 枚举

陈言必行

C# 六月月供

开发增效利器—IDEA辅助插件推荐

中原银行

开发 IDEA 插件 中原银行

数字化转型之数字资产知识库(springboot+es+vue+neo4j)

金陵老街

全文检索 知识图谱 Vue 3 spring-boot

spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)

程序员欣宸

Java spring Spring Framework 6月月更

耳东 2022 新的开始

耳东@Erdong

6月月更

Git远程操作详解

乌龟哥哥

6月月更

企业知识管理系统应具备的功能

小炮

被这个参数三杀了

hncscwc

hadoop 6月月更

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