运行更少的测试来激发快乐

2020-06-12 04:10:34

开发人员编写测试以确保正确性,并允许安全地进行未来的更改。然而,随着功能数量的增加,测试的数量也在增加。考试是一把双刃剑。一方面,编写良好的测试捕获错误并维护程序的稳定性,但随着代码库的增长,大量测试会阻碍可伸缩性,因为它们需要很长时间才能运行,并增加间歇性测试失败的可能性。软件项目通常要求在合并到MASTER之前通过所有测试。这增加了所有开发人员的开销。间歇性的测试失败加剧了问题的恶化。间歇性失败的一些原因是数据库中的不稳定性HTTP连接/模拟随机生成器将状态泄露给其他测试:测试每次都会自己通过,但其他测试会根据顺序而失败。不幸的是,不能完全消除间歇性失败的测试,并且随着代码库的增长,发生间歇性失败的可能性也会增加。它们使已经很慢的测试套件变得更慢,因为现在您必须重试它们,直到它们通过为止。我并不是说不应该编写测试。质量保证、性能监控和通过及早捕获错误(而不是在生产中)来加速开发的好处大于坏处。不过,我们可以作出改善。因此,我的团队开始了使我们的持续集成(CI)更稳定、更快的旅程。我将分享动态分析系统来选择我们实现的测试,然后是我们探索过但决定不采用的其他方法。考试选择给我的生活带来了欢乐。我希望我能给你们带来同样的快乐。ShopifyTests的测试问题阻碍了这里的开发人员的工作效率。我们的单片式存储库的测试套件:每年有超过150,000个测试以20-30%的大小增长,在数百个码头容器上并行运行大约需要30-40分钟。每个拉请求都需要通过所有测试。开发人员要么等待测试,要么为上下文切换付出代价。在我们一年两次的调查中,构建稳定性和速度是一个反复出现的话题。所以我们的开发人员清楚地感觉到了这个问题。用测试解决这个问题关于优化代码的博客文章/文章/研究论文很多,不幸的是,关于测试的文章很少。事实上,我们了解到优化测试是不现实的,因为纯粹的数量和增长。我们还了解到,对于许多公司来说,这是一个未知的领域。随着我们研究的进展,很明显,正确的解决方案是只运行与代码更改相关的测试。对于充分利用语言灵活性的大型动态类型Ruby代码库来说,这是一个挑战。此外,Rails中的元编程以及代码库中影响应用程序行为的其他非Ruby文件(例如YAML、JSON和JavaScript)加剧了这一困难。什么是动态分析?动态分析本质上就是记录每个方法调用。我们运行每个测试并跟踪调用图中的所有文件。一旦有了调用图,我们就创建一个测试映射:对于每个文件,我们都会找到哪些测试在其调用图中包含该文件。通过查看哪些文件已被修改、添加、删除或重命名,我们可以查找需要运行的测试。您可以签出Rotcope和Tracepoint来记录Ruby应用程序的调用图。为什么要使用动态分析?Ruby是一种动态类型的语言,我们不能使用静态分析检索依赖图。因此,我们不知道代码的相应测试。在铁路1上运行Ruby动态分析的同时。它很慢,生成调用图是计算密集型的,而且我们不能对每一个PR都运行它。取而代之的是,我们对每个部署的委托运行动态分析。映射可能滞后于HEAD生成的映射滞后于主服务器的头部,因为它是异步运行的。要解决此问题,我们将运行至少满足以下条件之一的所有测试:根据当前分支的代码更改在当前分支上添加或修改上一次生成的映射的头部和当前分支的头映射测试之间添加或修改的测试3。存在无法跟踪的文件在调用图上存在无法跟踪的非Ruby文件,如YAML、JSON等。我们向Rails添加了自定义补丁,以跟踪其中一些补丁。例如,我们修补了I18n::Backend类以跟踪YAML中的转换文件。对于尚未跟踪的文件更改,我们会运行每一个测试。元编程混淆调用路径我们在已知目录中添加了现有的元编程,并在文件路径上添加了全局规则以确定要运行哪些测试。我们不鼓励通过冰糕和其他链接进行新的元编程。一些失败的测试将不会被捕获动态分析生成的映射可能会与最新的主文件过期,有时失败的测试不会被选中并合并到主文件中。为了绕过这个问题,我们运行完整的测试套件e