阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

一种有效的测试策略

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

评论

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

模块一

吴科🍀

【Flutter 专题】89 图解基本 Overlay 悬浮新手引导

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 7月日更

Pandas高级教程之:GroupBy用法

程序那些事

Python 数据分析 pandas

NNB牛气冲天系统软件开发搭建

Rust从0到1-智能指针-Rc<T>

rust 智能指针 引用计数 smart pointer Rc<T>

初探 Redis 客户端 Lettuce:真香!

vivo互联网技术

Java 数据库 redis redis cluster lettuce

IPFS分布式存储矿机挖矿系统开发

获客I3O6O643Z97

挖矿 ipfs如何挖矿

前端智能化 or 低代码,也许不是个选择题

清秋

大前端 低代码 智能化

校友小程序图片安全审核方案与security.imgSecCheck不能校验超过1M图片的解决思路

CC同学

计算机基础知识 -- 位,字节,字符

转山转水

淘悠优软件系统开发内容

日更总结 6月日更 21天写作

万里无云万里天

6月日更

Python OpenCV 之图像梯度,Sobel 算子、Scharr 算子和 laplacian 算子

梦想橡皮擦

7月日更

深入理解搜索引擎——详解query理解

药老算法

搜索引擎 nlp query纠错 语义分析 意图识别

Swarm挖矿APP系统开发

获客I3O6O643Z97

挖矿挖什么币好 ipfs和swarm哪个更有价值

乐挖矿场软件系统开发案例

filecoin矿工的收益有哪些?

fil fil收益 ipfs挖矿

PHA项目挖矿平台系统开发App

获客I3O6O643Z97

挖矿矿池系统开发案例 PHA矿机挖矿 PHA质押挖矿

有哪些好用的团队文档和技术资料管理的工具?

万事ONES

在线文档 ONES 协同办公

通过运行期类型检查实现泛型算法

喵叔

7月日更

作业1

🐻🐻

架构师训练营第 1 期 作业一

SUN太阳币APP系统开发方案

免费分享Spring与SpringMVC开发的优秀图书

Java入门到架构

Java spring Java书籍推荐

有哪些适合大型系统的项目开发管理工具?

万事ONES

项目管理 研发管理 ONES

Takin Talks·上海 |开源后首场主题研讨会来了,一起解密Takin技术吧!

TakinTalks稳定性社区

目前有哪些好用的用例管理工具?

万事ONES

测试用例 ONES 测试管理

Docker 超详细版(基础+进阶)

若尘

Docker 容器 镜像

秒懂!进制和位运算

Ayue、

位运算 二进制

一文看懂filecoin挖矿的成本到底有哪些?

IPFS fil成本 fil挖矿

DOGT狗狗通证软件系统开发公司

揪出那个无主键的表

Simon

MySQL 主键

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