微服务架构中测试的复杂度进一步增加,了解测试的类型可用帮助我们尽早交付软件与保持软件高质量之间的平衡
1. 测试象限
面向技术的测试:首先帮助开发人员构建系统的测试,这个象限里的测试大都是可用自动化测试的,例如性能测试和小范围的单元测试
面向业务的测试:帮助非技术背景的人了解系统是如何工作的。
支持团队:帮助开发人员是否正确实现了功能,帮助业务人员了解系统是否实现了正确的功能
评价产品:非功能测试(响应时间,可扩展性,性能测试、安全测试)及可用性测试
2. 测试范围:测试金字塔
Mike Cohn在《Scrum敏捷软件开发》介绍了测试金字塔,描述了不同的自动化测试类型
金字塔模型可用帮组我们思考:1)不同的测试类型应该覆盖的范围;2)为不同的测试类型投入多大的比例
1) 单元测试:通常只测试一个函数和方法调用
a. 通过TDD(Test-Driven Design,测试驱动开发)写的测试
b. 基于属性(?)的测试技术所产生的测试
单元测试的主要目的:对于功能是否正常快速给出反馈;对于代码重构非常重要。
2) 服务测试:绕开用户界面,直接针对服务的测试
a. 独立应用程序中,服务测试可能只测试为用户界面提供服务的一些类
b. 包含多个服务的系统,一个服务测试只测试其中一个单独服务的功能
服务测试比单元测试的范围大,比单元测试更难定位问题。和端到端测试相比,服务测试包含的组件已经少多了。
3) 端到端测试:用户界面是系统功能的集成点,所以改成了端到端的测试。会覆盖整个系统,通常需要打开一个浏览器来操作GUI。
1) 权衡:
越靠近金字塔顶端
a. 优势:测试覆盖的范围越大;同时对被测试的功能也越有信心
b. 劣势:需要更长的时间运行测试,反馈周期变长。测试失败后,比较难定位哪个功能被破坏。
越靠近金字塔的底部:
a. 优势:测试会越快,反馈周期变短,测试失败后更容易定位被破坏的功能,持续集成的构建时间也很短
b. 劣势:只测试了一行代码,很难有充足的信心认为系统作为一个整体依然正常工作
当范围更大的测试(比如服务测试或者端到端的测试)失败后,会尝试一个单元测试来重现问题,以便能更快的速度捕获同样的错误。
2) 比例:
好的经验法则:顺着金字塔向下,下面一层的测试数量要比上面一层多一个数量级。
3. 实现服务测试
服务测试只要测试一个单独服务的功能,为了隔离其他的相关服务,需要一种方法给所有的外部合作者打桩。
1) 打桩,是指为被测试的服务的请求创建一些有着预测响应的服务
步骤:
a. 对于每一个下游服务,都需要一个打桩服务,在运行服务测试的时候启动它们;
b. 另外需要配置被测试的服务,在测试过程中连接这些打桩服务
c. 为了模拟真实的服务,需要配置打桩服务为被测试服务的请求发回响应
2) Mock与打桩
Martin Fowler把Mock和打桩统称为测试替身(Test Double),如何权衡两者,请参考《测试驱动的面向对象软件开发》
Mock和打桩的对比:
Mock的优势:
1) 会进一步验证请求本身是否被正确调用,如果与期望请求不匹配,测试便会失败
2) 验证预期的副作用是否发生
Mock的劣势:
1) 过度使用mock会让测试变得脆弱
3)智能的打桩服务
Mountebank(http://www.mbtest.org),可以发送命令告诉它需要打桩什么端口、使用哪种协议以及当收到请求时该响应什么内容
4. 端到端的测试
通过用户界面的测试覆盖其涉及的所有服务。从另外一个层面讲,用户界面也集成了其涉及的所有的服务。所以运行端到测试需要部署多个服务。
会经常碰到的两个问题:
1) 应该使用服务的哪个版本?是生产环境的版本还是全部使用新版本进行验证?
2) 不同的用户界面测试会有很多的重叠测试,会重复部署这些重叠的服务。有没有更好的办法处理?
方法:让多个流水线扇入(fan in)到一个独立的端到端测试的阶段
任意一个服务在任何时候只要发生变化,都会运行针对这些服务的测试。如果测试通过,便会触发端到端测试。
5. 端到端测试的缺点
1) 脆弱的测试:测试失败的原因有时和服务的功能没有任何关系
2) 测试缓慢:端到端测试需要大量的时间,进而会造成大量的堆积
3) 元版本:所有服务在这些版本下可以一起工作,为何不一起部署?为何不给整个系统使用同一个版本号?这样做旧丢弃了微服务的主要优势之一:独立于其他服务单独部署一个服务的能力
把多个服务一起部署不可避免地会导致服务的耦合
6. 消费者驱动契约的测试
替代端到端测试的一个解决方案
CDC:Customer-Driven Contract消费者驱动的契约
使用CDC时,我们会定义服务(或生产者)的消费者的期望者的期望。这些期望会变成对生产者运行的测试代码,成为生产者CI流水线的一部分。如果这些契约被破坏,生产者就无法部署。另外,只运行CDC测试,周期更短,也更可靠。
1) Pact:CDC测试的工具,现在已经开源,使用Ruby语言编写,现在支持包括JVM和.Net的版本
a. 使用Ruby DSL来定义生产者的期望
b. 启动一个本地mock服务器,并对其运行期望来生成Pack规范文件(标准的JSON规范)
c. 在生成者端,使用JSON Pact规范来驱动对生产者API的调用
d. 验证响应来测试消费者的规范是否被满足
7. 跨功能的测试
非功能性需求,是对系统展现的一些特性的一个总结的术语,包括一个网页的响应时间,系统可以支持的用户数量等。
使用跨功能需求(CFR, Cross-Functional Requirement)来替换非功能需求,因为这些系统行为是很多横切工作融合的结果。
很多CFR只能在生产环境测试。
8. MTTR优于MTBF,平均修复时间优于平均故障间隔时间
MTBF:Mean Time Between Failures,平均故障间隔时间
MTTR:Mean Time To Repair,平均修复时间
减少修复时间的技术:良好的监控+尽快回滚。使用监控尽早发现生产中的问题,尽快回滚减少对客户的影响。