测试一下:我们如何确定在拉取请求检查中运行哪些测试

哈Ha! 我叫Egor Danilenko。 我正在为Sberbank Business Online的公司网上银行开发数字平台,今天我想向您介绍我们采用的CI开发流程。

开发人员更改如何注入到发行分支中? 开发人员在本地进行更改,并加入我们的版本控制系统。 我们将Bitbucket与作者的插件结合使用(我们在前面的内容中介绍了该插件)。 在这些更改上,将启动组装并跟踪测试(单元,集成,功能)。 如果程序集没有失败并且所有测试都成功通过,以及在成功进行检查之后,则将请求请求倾倒到主分支中。

但是随着时间的流逝,团队的数量在增加。 测试数量成比例增长。 我们知道,如此众多的团队将加速“拉慢请求检查”问题的发作,并且将不可能开发产品。 目前,我们大约有40个团队。 它们与新功能一起带来了新的测试,这些测试也需要在请求请求上运行。

我们认为,如果我们知道要运行哪些测试来更改特定的代码,那将很酷。

这就是我们解决这个问题的方式。

问题陈述


有一个包含测试的项目,我们想确定在“触摸”某个文件时需要运行哪些测试。

我们都知道EclEmma JaCoCo代码覆盖库。 我们以此为基础。

关于JaCoCo


JaCoCo是一个用于通过测试衡量代码覆盖率的库。 这项工作基于对代码字节的分析。 代理收集执行信息,并在JVM请求或关闭时将其上载。

数据收集有三种模式:

  1. 文件系统:停止JVM之后,数据将被写入文件。
  2. TCP套接字服务器:您可以将外部工具连接到JVM并通过套接字接收数据。
  3. TCP套接字客户端:启动后,JaCoCo代理将连接到特定的TCP端点。

我们选择了第二个选项。

解决方案


必须具备使用JaCoCo代理运行应用程序和进行测试的能力。

首先,我们增加了使用JaCoCo代理运行测试的能力。

可以启动Java代理:

-javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2] 

向我们的项目添加一个依赖项:

 dependencies { compile 'org.jacoco:org.jacoco.agent:0.8.0' } 

我们只需要从代理开始就可以收集统计信息,因此我们将withJacoco标志(默认值为false)添加到gradle.properties中。 我们还指定收集统计信息,地址和端口的目录。

将带有代理的jvm参数添加到测试启动任务:

 if (withJacoco.toBoolean()) { … jvmArgs "-javaagent:${tempPath}=${jacocoArgs.join(',')}".toString() } 

现在,每次成功完成测试后,我们都需要通过JaCoCo收集统计信息。 为此,编写TestNG侦听器。

 public class JacocoCoverageTestNGListener implements ITestListener { private static final IntegrationTestsCoverageReporter reporter = new IntegrationTestsCoverageReporter(); private static final String TEST_NAME_PATTERN = "%s.%s"; @Override public void onTestStart(ITestResult result) { reporter.resetCoverageDumpers(String.format(TEST_NAME_PATTERN, result.getInstanceName(), result.getMethod().getMethodName())); } @Override public void onTestSuccess(ITestResult result) { reporter.report(String.format(TEST_NAME_PATTERN, result.getInstanceName(), result.getMethod().getMethodName())); } } 

将监听器添加到testng.xml并将其注释掉,因为在正常的测试运行中我们不需要它。

现在,我们有机会使用JaCoCo代理运行测试,并将收集每个成功的测试统计信息。

有关如何实施报告程序以收集统计信息的更多信息。
在报告程序初始化期间,将与代理建立连接,并创建一个目录,用于存储统计信息并收集统计信息。

添加报告方法:

 public void report(String test) { reportClassFiles(test); reportResources(test); } 

reportClassFile方法在统计信息目录中创建jvm文件夹,其中存储了由类文件收集的统计信息。

reportResources方法创建资源文件夹,该文件夹存储所收集的资源统计信息(用于所有非类文件)。

该报告包含用于连接到代理,从套接字读取数据以及写入文件的所有逻辑。 由JaCoCo提供的工具实施,例如org.jacoco.core.runtime.RemoteControlReader / RemoteControlWriter。

reportClassFiles和reportResources函数使用通用的dumpToFile函数。

 public void dumpToFile(File file) { try (Writer fileWriter = new BufferedWriter(new FileWriter(file))) { for (RemoteControlReader remoteControlReader : remoteControlReaders) { remoteControleReader.setExecutionDataVisitor(new IExecutionDataVisitor() { @Override public void visitClassExecution(ExecutionData data) { if (data.hasHits()) { String name = data.getName(); try { fileWriter.write(name); fileWriter.write('\n'); } catch (IOException e) { throw new RuntimeException(e); } } } }); } } } 

该函数的结果将是一个文件,该文件具有此测试会影响的一组类/资源。

因此,在运行所有测试之后,我们有了一个包含类文件和资源统计信息的目录。

剩下的事情是为日常统计收集编写一个管道,并向拉入请求检查的管道添加添加。

我们对项目组装的阶段不感兴趣,但是我们将更详细地考虑发布统计信息的阶段。

 stage('Agregate and parse result') { def inverterInJenkins = downloadMavenDependency( url: NEXUS_RELEASE_REPOSITORY, group: '', name: 'coverage-inverter', version: '0', type: 'jar', mavenHome: wsp ) dir('coverage-mapping') { gitFullCheckoutRef '', '', 'coverage-mapping', "refs/heads/${params.targetBranch}-integration-tests" sh 'rm -rf *' } sh "ls -lRa ..//out/coverage/" def inverter = wsp + inverterInJenkins.substring(wsp.length()) sh "java -jar ${inverter} " + "-d ..//out/coverage/jvm " + "-o coverage-mapping//jvm " + "-i coverage-config/jvm-include " + "-e coverage-config/jvm-exclude" sh "java -jar ${inverter} " + "-d ..//out/coverage/resources " + "-o coverage-mapping//resources " + "-i coverage-config/resources-include " + "-e coverage-config/resources-exclude" gitPush '', '', 'coverage-mapping', "${params.targetBranch}-integration-tests" } 

在coverage映射中,我们需要存储文件名,并在其中存储需要运行的测试列表。 由于统计信息收集的结果就是测试的名称,它存储了一组类和资源,因此我们需要将整个过程取反,并排除不必要的数据(第三方库中的类)。

我们反转统计数据并推送到我们的存储库。

每晚收集统计数据。 它存储在每个发行分支的单独存储库中。

宾果!

现在,在运行测试时,我们只需要查找修改后的文件并确定需要运行的测试。

我们遇到的问题:

  • 由于JaCoCo仅适用于字节码,因此无法从框中收集有关.xml,.gradle,.sql等文件的统计信息。 因此,我们必须“加快”我们的决定。
  • 持续监控统计信息和程序集的频率的相关性,如果每晚程序集由于某种原因失败,则将使用“昨天”的统计信息在拉动请求中进行验证。

Source: https://habr.com/ru/post/zh-CN430270/


All Articles