Pipeline作为整个软件交付流程重要的一环,如果每次构建资源占用比例过高,会导致大量的代码积压在开发环境等待构建、验证和打包,“在制品”数量也越来越多,而过多的“在制品”恰恰就是软件交付延期的隐形杀手之一。解决对Pipeline资源占用比例过高的途径只有一个:加速处理“在制品”的流程。在改进的过程中我们总结出了以下几种主要的加速手段: 1.并行化 假设Pipeline单元测试需要30分钟才能运行结束,可以通过切分单元测试多进程并发执行,如下图所示,可以节省近20分钟的运行时间: 同样也可以把并行化用于优化Pipeline结构,如下图所示,通过减少不必要的Pipeline依赖关系,让不同的Pipeline并行执行,可以减少大约五分钟的端到端的构建时间。 优化前: 端到端构建时间20分钟 优化后:端到端构建时间15分钟 2.使用Mock或者Stub,隔离真实服务 对于有数据库依赖的单元测试,如果在运行期间连接真实的数据库,读写速度会比较慢,除了IO操作之外,为了保证不同测试之间的隔离性,往往还需要考虑测试运行之后的数据清理工作,而这也会带来一部分的性能损耗。针对这种情况,可以考虑使用内存数据库替代真实的数据库,在提升IO操作的同时,数据清理工作也变得很简单。 为了保证代码的修改没有破坏现有的功能,一般我们会增加基于UI的回归测试,在测试运行之前部署当前应用以及其所依赖的各个服务。对于应用依赖的服务的部署和API调用,也会消耗部分时间。这是可以考虑使用Sinatra 、Moco等工具隔离部分第三方服务,从而缩短部署时间和API的调用时间。 但是隔离真实服务的同时也掩盖了测试替身和真实组件之间的差异性,比如我们在API测试中使用Sqlite替代SQL Server,但是SQLite并没有datatime字段类型,需要在测试代码中需要做额外的映射配置,这种差异性同样也会导致潜在的产品缺陷。所以我们在选择Mock或者Stub时需要权衡利弊,如果使用则需要额外的手段来验证这种差异性。 3.优化基础设施和运行环境 增加硬件配置如CPU,内存、替换固态硬盘等也可以一定程度地降低Pipeline的构建时间。对于由于语言或者框架本身带来的性能约束,也可以通过升级到新版本来解决,比如把Ruby从1.8.7版本升级到2.0版本。 提高构建成功率 团队在改进的过程中发现可以通过下面的公式大致估算出平均每天Pipeline产出的可用包的数量: 根据这个公式,如果要增加平均每天出包的数量,除了降低每次构建的时间之外还需要提高Pipeline的构建成功率,而影响构建成功率最常见的问题就是“非确定性”测试。在项目的Pipeline上曾经出现过下面这些情况: 一些UI测试每次至少需要被重新执行一次才能通过 部分单元测试在特定的时间段会稳定失败 构建结构和测试被执行的顺序有关 Martin Fowler在Eradicating Non-Determinism in Tests中指出了这种非确定性测试存在的两个问题:首先它们属于无用测试,由于测试本身的不确定性,它们已经无法用来描述、验证对应的功能。测试运行的结果也无法给开发人员提供正确的反馈,如果测试失败,开发人员无法直接判断这个测试是由于产品缺陷导致还是由于非确定行为导致。其次这些测试就像“致命的传染病菌”一样,降低正常测试的存在价值。假设一个测试套件中有100个测试,其中10个测试为非确定性测试,这些非确定测试会给开发团队带来很多的“噪音”,团队对于Pipeline失败会觉得司空见惯、习以为常,剩余90个测试的作用也会大打折扣。 1.保证隔离性 在Pipeline的结构方面,由于非确定性测试的“传染性“,在着手解决非确定性测试之前可以考虑从测试套件中隔离这种类型的测试,这种隔离一方面可以保证正常的测试可以继续提供正确的反馈,另一方面也方便开发人员解决非确定性的测试问题,如果被隔离的测试失败,只需要重新执行部分测试而不是整个测试套件,很大程度地缩短了修复过程中的反馈周期。 (责任编辑:本港台直播) |