「开发者测试」泛指开发者从事的所有与测试自动化相关的技术活动,其目标在于验证被测系统的预期行为和状态。一个完备的开发者测试系统,包括如下4个基本元素:
- 被测系统(SUT):粒度大可至系统,小可至类或函数;
- 外部依赖(DOC):被测系统所依赖的其他系统、服务、组件和数据(DOC);
- 测试用例集:一组使用宿主语言或脚本语言编写的测试用例;
- 运行时环境:运行时创建的测试执行过程。
开发者测试面临的挑战
在大型系统中实施开发者测试,是一项极为挑战的技术实践,尤其在遗留的大型系统中。在这些系统实现中,基本存在一些共同的特征。
- 系统规模庞大,系统复杂度高
- 存在大量未被测试覆盖的实现代码
- 头文件包含关系错综复杂
- 系统中组件间的耦合度高
- 缺失领域模型的显式表达
- 大量的全局变量直接访问
- 随处可见的重复代码
可以想象,在如此现实存在的系统实现中,开展开发者测试是极具挑战的。例如,隔离编译和构建,因为混乱的头文件包含关系,让开发者痛不欲生,欲罢不能。
再举个例子,当开发者构建测试上下文,需要配置复杂的数据,及其构造复杂的场景。开发者发现一个怪状,与系统实现相比,构造测试用例更为复杂。在这样的状况下,导致开发者大概率放弃开发者测试。
为什么会出现这些问题呢?
开发者测试的技术债务
实际上,开发者测试与软件系统的设计存在千丝万缕的关联关系。系统架构、设计、可扩展、可测试性、可理解性等,往往都是一环扣一环,任何一环出现问题,都将制约其他环节。
在遗留系统实现中,存在大量未被测试用例覆盖的代码实现,实践开发者测试并非一件容易的事情。由于长期累积的技术债务,系统设计的耦合度日益增加,最终导致整个软件系统完全不满足可测试性的基本要求。
另外,实践开发者测试的收益和技能在社区长期地被低估,导致开发者测试在很多的系统实现中未能取得良好的效果和进展。一方面,因为以往低估或忽略了开发者测试的潜在价值和收益;另一方面,而是因为缺乏开发者测试技能的储备和实践经验的累积。
开发者测试的设计技能
实践开发者测试需要扎实的软件设计的基本功,及其需要对领域模型的深刻理解。如果组件间是耦合的,实施组件测试,必然导致测试边界模糊不清;如果对领域知识把握得不够准确,则很难设计出稳定的测试用例;如果没有对组件的依赖设计出可替换换的架构,必然导致依赖很难在测试系统中隔离;如果没有方便快捷的构建系统,是不能满足测试系统快速反馈的基本诉求。
总而言之,开发者不仅包括设计和实现系统的基本技能,还要包括开发和实现可测试系统的基本技能。
- 挖掘领域知识
- 可测试性的设计
- 依赖隔离和替换
- 识别组件边界
- 设计构建系统
开发者测试的用例设计
开发者设计和实现的测试用例的要求在业界往往被低估。例如,系统实现的代码往往存在严格的准入条件,例如Code Review,重复率检查等机制,但往往忽略测试用例代码的质量要求,这会打破窗户,让维护测试的成本增大,甚至放弃测试自动化。
一个优秀的测试系统,不仅仅只是能够工作,而是能够长期伴随软件开发的生命周期,提供快速有效地反馈。笔者见到过很多测试系统,或者充斥着大量重复的测试用例,或测试用例极为复杂,或测试用例极为脆弱,或测试用例的可读性极差,或测试反馈周期过长等等问题。
因此,对于设计测试用例,也需要掌握设计测试用例的基本技能。
- 设计秒级反馈的系统
- 设计正交的测试用例
- 设计稳定的测试用例
- 消除重复的测试用例
- 有效地组织测试用例
- 准确地命名测试用例
后文,我将通过一系列文章,详细讲述开发者测试所面临的挑战,及其应对的基本方法。