软件测试驱动开发全解析
一、测试人员与功能测试
测试人员、业务分析师、架构师、领域专家和 UI 专家等角色可共同探讨规范细节,并创建能展示特定场景的测试用例,这些场景可被执行和验证。在功能测试方面,应让测试人员编写测试用例,而不是由程序员来做。若程序员编写功能测试或手动运行验证测试,往往会导致不良后果。测试人员编写功能测试,程序员协助将其集成以实现自动化运行,这才是更合理的分工。
测试驱动设计和代码的目的并非追求完美,而是为了获得更好的设计,实现快速可靠的回归测试。不过,这种方法并不能保证零缺陷。当发现或有人报告缺陷时,编写因该缺陷而失败的测试用例,然后修复代码并确保所有测试通过,这样能防止该缺陷在未来再次进入代码。
同时,要定期关注代码覆盖率。进行测试驱动设计的人员通常无需纠结于合适的覆盖率数值。有时覆盖率报告可能显示某些代码未被任何测试(单元测试、功能测试或集成测试)覆盖,此时应果断移除这些未测试的代码,尤其是在开发过程中发现时,这样做相对容易。若等到后期,由于担心移除未测试代码会破坏系统,操作会变得困难。即便认为某些代码绝对必要,也可先移除,再采用测试优先的方式重新实现,这样能更有信心地确保有测试验证该代码的行为。
二、团队领导和架构师的角色
团队领导和架构师在团队编写自动化测试的能力方面起着至关重要的作用。因为设计和架构对可测试性有重大影响,而且开发人员会向他们寻求技术实践方面的指导。以下是他们可以协助团队进行测试驱动开发的一些方式:
1.
优先考虑可测试性
:架构和设计决策涉及多个关键标准,如可扩展性、可靠性、可扩展性、安全性和性能等,可测试性应在创建和审查架构与设计时都列为首要优先事项。例如,创建服务时,设计一个模拟服务,以便轻松测试依赖该服务的组件;若应用程序严重依赖第三方服务,了解这些服务是否提供模拟版本,若没有,则考虑通过适配器路由调用,这样适配器可轻松被模拟,便于测试依赖该外部服务的部分。
2.
支持代码审查
:团队常采用持续战术代码审查的做法,鼓励程序员每天相互审查代码。团队领导和架构师在此过程中定期审查,并在出现问题时提供指导。同时,鼓励团队在审查代码的同时审查测试用例,审查时可检查测试的质量、一致性、复杂性和清晰度,确保测试级别合适,查找缺失的测试,如边缘情况测试、负面或异常测试等。
3.
用测试表达需求
:鼓励团队将各种假设、约束、功能和非功能需求表达为测试用例,这些测试可作为系统中各种架构和设计决策的文档和验证器。例如,发票付款收据仅对相关方可见的要求,以及订单处理模块在与库存服务通信时应优雅处理网络故障的要求,都应通过一系列测试来表达和验证。
4.
以测试解决疑问
:项目中的人员常对系统处理特定场景的方式表示怀疑,此时应编写测试用例来验证,而不是争论或寻求解释。测试能明确应用程序是否按预期工作,避免不必要的争论和猜测。
三、经理的职责
团队通常期望经理提供支持和指导,致力于质量和技术卓越的经理能对团队的成功产生更大影响。以下是经理可采取的措施,以激励和促进团队在自动化测试方面取得成功:
1.
推广可持续的敏捷开发实践
:大多数组织已采用或正在采用敏捷开发,敏捷宣言的一些原则包括:欢迎需求变更,即使在开发后期;提供团队成功所需的环境和支持;过程应促进可持续发展;持续关注技术卓越和良好设计可增强敏捷性。敏捷开发并非追求速度,而是要采用能实现可持续速度的技术实践,必要时放慢速度以在可能的地方提高速度。经理和组织领导者负责管理进度和期望,为团队提供环境,使其以合理、可持续的节奏创建相关、可维护的工作软件。许多组织过于关注短期目标和当前版本的截止日期,忽视了团队积累的技术债务,导致短期收益和长期损失。
2.
妥善处理遗留应用程序
:在实际项目中,常遇到几乎没有测试的遗留应用程序。强制要求用自动化测试验证所有现有代码可能有害,因为为现有代码编写测试很困难,且短时间内编写大量测试会降低测试质量,产生虚假的信心。可采取以下方法处理遗留代码:
- 对为遗留应用程序编写的新代码进行测试驱动开发,逐步重构与变更相关的代码部分,开始编写自动化测试,由于关注范围小,可创建高质量的测试。
- 在修复缺陷之前或之后编写测试,当代码上下文清晰时,编写的测试更有意义。
- 对于业务关键或频繁变更的代码部分,安排团队评估编写自动化测试的工作量,可能需要先重构代码,再评估编写测试的好处与修改关键代码的风险。
- 处理遗留应用程序时,鼓励团队设置脚本,若测试覆盖率低于当前水平则使构建失败。例如,某客户的遗留应用程序覆盖率仅 26%,设置构建在覆盖率低于前一次构建时失败,六个月后覆盖率大幅提高,享受到更多自动化测试的好处。
3.
避免产生新的遗留应用程序
:如今的新项目可能成为未来的遗留项目,经理和组织领导者可采取以下措施营造有利于创建可维护软件的环境:
- 从当前经验中学习,衡量修复缺陷的时间、精力和成本,并将其纳入开发时间和成本,从局部优化思维转变为全局优化思维。
- 公开张贴测试指标和覆盖率,在办公室休息室的屏幕上循环显示持续集成仪表盘,传达团队对编写测试和保持高覆盖率的重视。对于测试数量或覆盖率低的项目,显示趋势可激励团队努力改进;若数据不佳,可提醒团队。团队还可快速看到表现较好的项目并寻求建议。
- 注意避免发出混淆的信息,团队常抱怨被要求编写自动化测试,但未得到足够的时间、培训和资源。若团队对新技术或自动化测试不熟悉,需要时间来适应和提高效率,应评估团队当前技能,制定计划使其具备测试驱动开发的能力。
- 为团队提供所需的指导,公司可聘请敏捷教练帮助团队采用敏捷开发,成功的团队还会聘请技术教练帮助程序员和测试人员正确采用可持续的技术实践。也可指定一位倡导者或自动化验证负责人,协助评估和采用自动化测试。
- 改变“完成”的定义,使其不仅包括交付代码,还包括交付具有有用自动化测试的有价值功能。将自动化测试纳入“完成”的定义,可改变团队的思维和运作方式,促使他们重新思考估算和承诺交付的内容。
四、相关技术和工具资源
以下是一些相关的技术和工具资源:
| 资源名称 | 链接 |
| ---- | ---- |
| 3 - As 模式 | http://c2.com/cgi/wiki?ArrangeActAssert |
| 敏捷软件开发宣言 | http://www.agilemanifesto.org |
| Angular 框架 | https://angularjs.org |
| Chai 断言库 | http://chaijs.com |
| Express 轻量级 Web 框架 | http://expressjs.com |
| Mocha JavaScript 测试框架 | https://mochajs.org |
| Sinon 测试替身库 | http://sinonjs.org |
五、测试驱动开发流程 mermaid 图
graph LR
A[发现或报告缺陷] --> B[编写因缺陷失败的测试用例]
B --> C[修复代码]
C --> D[确保所有测试通过]
D --> E[防止缺陷再次进入代码]
通过以上全面的测试驱动开发方法和相关措施,能帮助团队创建高质量、可维护的代码,实现可持续的软件开发。
expect(success).to.eventually.be.true;
这句代码表达了通过坚持、努力和纪律可取得成功的信念。希望大家能在软件开发中积极应用这些方法,提升软件质量。
软件测试驱动开发全解析(续)
六、不同角色在测试驱动开发中的作用总结
在测试驱动开发中,不同角色都有着各自独特且重要的作用,具体总结如下表所示:
| 角色 | 主要作用 |
| ---- | ---- |
| 测试人员 | 编写功能测试用例,确保测试用例能覆盖特定场景,与其他角色共同探讨规范细节 |
| 程序员 | 协助测试人员将功能测试集成以实现自动化运行,编写代码修复缺陷并确保测试通过 |
| 团队领导和架构师 | 优先考虑架构和设计的可测试性,支持代码和测试用例审查,鼓励用测试表达需求,以测试解决疑问 |
| 经理 | 推广可持续的敏捷开发实践,妥善处理遗留应用程序,避免产生新的遗留应用程序,为团队提供资源和指导 |
七、测试驱动开发中的关键技术和操作要点
-
异步函数和测试
- 异步函数测试 :对于异步函数的测试,需要特殊处理。例如,测试异步函数时,可使用特定的测试框架和断言库。以下是一个简单示例,展示如何测试异步函数:
// 异步函数示例
function asyncFunction(callback) {
setTimeout(() => {
callback(null, 'Success');
}, 100);
}
// 测试异步函数
it('should handle async function correctly', (done) => {
asyncFunction((err, result) => {
if (err) {
done(err);
} else {
expect(result).to.equal('Success');
done();
}
});
});
- **异步测试(针对 Promise)**:当处理 Promise 时,测试方法也有所不同。可以使用 `chai-as-promised` 等库来辅助测试。示例如下:
// Promise 函数示例
function asyncPromise() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Promise Success');
}, 100);
});
}
// 测试 Promise
it('should handle async promise correctly', async () => {
const result = await asyncPromise();
expect(result).to.equal('Promise Success');
});
-
依赖处理
- 依赖注入 :在代码中,为了提高可测试性,常常使用依赖注入的方式。例如,在 AngularJS 中,可通过以下方式注入依赖:
// AngularJS 控制器依赖注入示例
angular.module('myApp', [])
.controller('MyController', ['$scope', 'myService', function($scope, myService) {
// 使用注入的服务
$scope.data = myService.getData();
}]);
- **使用测试替身**:当处理外部依赖时,可使用测试替身(如 Sinon)来模拟依赖。示例如下:
const sinon = require('sinon');
const myModule = require('./myModule');
describe('MyModule', () => {
it('should call the external service correctly', () => {
const stub = sinon.stub(myModule, 'externalService').returns('Mocked Data');
const result = myModule.doSomething();
expect(result).to.equal('Mocked Data');
stub.restore();
});
});
八、测试驱动开发的整体流程 mermaid 图
graph LR
A[项目启动] --> B[团队角色分工]
B --> C[架构和设计(考虑可测试性)]
C --> D[编写测试用例(测试人员主导)]
D --> E[编写代码(程序员)]
E --> F[代码审查(团队成员)]
F --> G[测试执行(自动化)]
G --> H{测试是否通过}
H -- 是 --> I[持续集成和部署]
H -- 否 --> J[修复代码(程序员)]
J --> D
I --> K[监控和维护(持续关注代码覆盖率等)]
K --> L{是否发现新缺陷}
L -- 是 --> D
L -- 否 --> M[项目持续推进]
九、总结
测试驱动开发是一种有效的软件开发方法,它涉及多个角色的协同合作,包括测试人员、程序员、团队领导、架构师和经理等。每个角色都在不同方面发挥着重要作用,共同推动项目朝着高质量、可维护的方向发展。
在技术层面,需要掌握异步函数和 Promise 的测试方法,合理处理依赖关系,使用测试替身等工具来提高测试效率和代码可测试性。同时,要重视代码覆盖率的监控,及时处理遗留应用程序,避免产生新的遗留问题。
通过遵循测试驱动开发的流程和方法,结合相关的技术和工具,我们可以更加自信地面对软件开发中的各种挑战,创建出满足用户需求的高质量软件。希望大家在实际开发中能够充分运用这些知识和技能,不断提升软件开发的水平。
expect(success).to.eventually.be.true;
让我们一起为实现高质量的软件开发而努力!
超级会员免费看
1584

被折叠的 条评论
为什么被折叠?



