在最近的一个大型项目中,咱们在早期就定下了一个目标:不会在软件中使用大量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 测试登陆到应用程序中、建立一个订单,而后我能够验证是否产生了一个订单请求,那么我会感受很良好。
关于全系统测试的一个常见误区是认为它就是黑盒测试。然而,全系统测试的特色在于,你必须对系统内部发生的事情了如指掌。实际上,全系统测试甚至还能够利用域模型来生成数据,而不是一个纯粹为测试目的而构建的系统后门。不少团队容易掉进去的一个大坑是,不按照与生产环境同样的代码路径来测试,这将致使系统处于古怪的、无效的、很难处理的状态。
在咱们的项目中,全系统测试代码是咱们在声称一个功能/故事作完以前的所写的最后代码。对于描述一个功能的“已完成”特性来讲,手工测试的成本过高、并且不可靠。可是,若是我能像在生产环境同样去测试,经过彻底同样的外部界面来完成,那这样的手工测试也算是成功的。
全盘考虑
在一个未经测试过的应用程序中,做为提升覆盖率的手段,咱们发现实际上最有价值的测试策略是从全系统测试开始,而后往下移,直至单元测试。咱们先作最宽松、最简单的断言,而后慢慢往下移,直至单元级别的逻辑。在全新开发的应用程序中,咱们倾向于不仅是特别关注某一个区域,由于对于一个须要长期维护的系统来讲,全部的测试都很重要。
这种测试策略确实须要必定的投资。咱们发现,当咱们知道这个应用程序对客户的业务有决定性做用的时候,这样的全盘考虑在特别有效。若是一个应用程序对业务有决定性做用,那它将不得不面临变动。当变动来临的时候,咱们最好能安全地实施变动而不影响客户的业务。