Cucumber Parallel Run

篇幅比较长,列个提要吧:

  • 背景
  • 核心思想
  • 方案一
  • 方案二
  • 方案三
  • 方案四
  • 执行策略
  • 执行结果
  • POM 详细配置

背景:

日积月累 Smoke + Regression Test Cases 总数达 1 万+,运行 4 个半小时之久。正常情况,晚上跑完,第二天上班便可 triage,也不耽误。但总会遇到意外情况,如 VMs disconnection issue, 或是其他国外团队深夜暗戳戳 deploy 新版本,再或 504 gateway timeout。。。等等不确定因素,要是下午时间 trigger一把,作息 965 的我们就不能保准能顺利出 Triage Report, 基本就放弃了,哈哈!遇到临近上线关键时期,特别是有重大 Bug 修复,Leader 没有办法也只能人工吭哧吭哧了,个人觉得坚信 Automation 比人工更靠谱。到了真正为团队做贡献的时刻到了,哈哈,缩短运行时间不就能完美解决这个问题,而且是一劳永逸,以上废话比较多。

说干就干,趁 Sprint 开始没啥紧急 Task,花了 2 天时间研究一下 Parallel Run 方案,深入研究才发现有多种方案可以实现,但能只有一种满足我的需求,下面我会罗列 4 种方案,分析其中优劣。

环境:Maven + Cucumber + Java

核心思想:

首先要明白 Maven 是怎样执行 Cucumber cases
就要了解 Surefire Plugin,说白了它就是一个 Test Runner, 测试运行器。

The Surefire Plugin is used during the test phase of the build lifecycle to execute the unit tests of an application.

那么 Surefire 怎么运行 Cucumber cases 呢,请参考 官网,通过执行 Cucumber JUnit Runner, 依赖 Junit 和 cucumber-junit puglins。Junit 就是充当一个桥梁,解析 Cucumber feature files,执行 steps implement,并汇总执行结果,生成 Report。

JUnit is an open source unit testing framework for the Java programming language.

下面就是一个 Cucumber Junit Runner Class,@CucumberOptions annotation 就是用来进行相关配置的,有关细节可以参考 Cucumber官网。

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/features",glue = {"com.company.infra.services.stepdefs"},format = {"pretty", "html:target/site/cucumber-pretty", "json:target/cucumber.json"},tags = {"not (@archive or @ignore or @manual)"}
)
public class AllTest {// This class is empty because the options above fulfill all needs.
}

SureFire 怎么识别到 Runner Class Files
Filtering by Test Class Names for Maven Surefire

The Maven Surefire Plugin will scan for test classes whose fully qualified names match the following patterns.
**/Test*.java
**/*Test.java
**/*Tests.java
**/*TestCase.java
Moreover, it will exclude all nested classes (including static member classes) by default. Note, however, that you can override this default behavior by configuring explicit include and exclude rules in your pom.xml file. For example, to keep Maven Surefire from excluding static member classes, you can override its exclude rules.

默认情况下,maven-surefire-plugin 的 test 目标会自动执行测试源码路径(src/test/java/)下所有符合上面命名模式的测试类,当然也可以在 POM File 中配置。

通常创建一个 Default Cucumber Junit Runner Class,那就是串行跑所有的 cases。那么要实现 Parallel Run,就等同于创建多个 Runner Classes,由 Surefire Plugin 并行执行这些 Rnnner Classes。

方案一

Cucumber 4.0.0 及以后版本支持 Parallel Run,详情请参考 Parallel Execution

Cucumber can be executed in parallel using JUnit and Maven test execution plugins. In JUnit the feature files are run in parallel rather than scenarios, which means all the scenarios in a feature file will be executed by the same thread. You can use either Maven Surefire or Failsafe plugin to execute the runners.

注意是 Feature level 的 Parallel Run,不是 Scenarios Level 的。

这个方案看上去不错,然后并不适合我们,我们用的 Cucumber JVM 版本是 2.4,我也尝试过升级到 4.0.0,发现有些 steps 并不兼容,可能有其它 dependencies,且这些出问题的 features 并不是我们 Owned,国外其它团队,出于谨慎,我决定另行出路。

方案二

苦力活,为每一个 feature 创建一个 Cucumber Junit Runner Class,这样做失优雅,哈哈!我们有大概 200 多个 feature files,大量的 Copy&Paste 工作不说,还得 Push 到 git上,这一串串无它用的 Class Files,真难看,放弃,还是尝试找个能自动生成 Runner Class的开源插件吧。

方案三

cucable-plugin 终于找到一款能自动 generate runner。

How it works

Cucable will cut up feature file into the smallest possible runnable scenarios
Each generated feature file includes a single scenario
After this, the runner classes for those generated features are generated based on a provided template file, either         
--->one runner per generated "single scenario" feature file 
or
--->one runner per group of "single scenario" feature files

有两种 Generate Runner 模式:
One runner per generated scenario
为每一个 Scenario 生成一个 feature file 并 generate一个 Runner Class file,如下图。
在这里插入图片描述
One runner per group of generated scenarios
还是为每一个 Scenario 生成一个 feature file,创建一个 Runer Class,运行多个 Features。可以通过配置 desiredNumberOfRunners or desiredNumberOfFeaturesPerRunner option
在这里插入图片描述

首先需要准备一个 Template Cucumber Junit Runner Class file. Cucable Plugin 就是根据这个模板 Runner 为 generate 的 feature files 生成多个 Runner Files。

import cucumber.api.junit.Cucumber;
import cucumber.api.CucumberOptions;
import org.junit.runner.RunWith;@RunWith(Cucumber.class)
@CucumberOptions(features = {"target/parallel/features/[CUCABLE:FEATURE].feature"},plugin = {"json:target/cucumber-report/[CUCABLE:RUNNER].json"}
)public class CucableJavaTemplate {
}

了解一下 cucable plugin 相关配置

<plugin><groupId>com.trivago.rta</groupId><artifactId>cucable-plugin</artifactId><version>${cucable-plugin.version}</version><executions><execution><id>generate-test-resources</id><phase>generate-test-resources</phase><goals><goal>parallel</goal></goals></execution></executions><configuration><!-- Required properties --><sourceRunnerTemplateFile>src/test/resources/parallel/cucable.template</sourceRunnerTemplateFile><sourceFeatures>src/test/resources/features</sourceFeatures><generatedFeatureDirectory>src/test/resources/parallel/features</generatedFeatureDirectory><generatedRunnerDirectory>src/test/java/parallel/runners</generatedRunnerDirectory><!-- Optional properties --><numberOfTestRuns>1</numberOfTestRuns><includeScenarioTags>@includeMe and @includeMeAsWell</includeScenarioTags>                                <logLevel>compact</logLevel><desiredNumberOfRunners>2</desiredNumberOfRunners>                                <!-- or <desiredNumberOfFeaturesPerRunner>5</desiredNumberOfRunners> --></configuration>    
</plugin>

必要参数,从名字就能看出,就不解释了,来了解一下比较重要的可选参数。
numberOfTestRuns
默认值为1, 如果值设置 n 次,那么这个 Scenario 将反复运行 n 次,也就为此生成多个 feature Files,其实只是一同一个 Scenario 而已,建议慎用

This can be used if specific scenarios should be run multiple times. If this options is not set, its default value is 1.

For each test run, the whole set of features and runners is generated like this:

MyFeature_scenario001_run001_IT.feature
MyFeature_scenario001_run002_IT.feature
MyFeature_scenario001_run003_IT.feature
etc.

parallelizationMode
cucable 是支持 Scenario Level Mode 和 Feature Level Mode 的,但是如果用 Feature Level Mode, 那么 tag filter 将失效,这是个重要信息。

By default, Cucable uses the parallelizationMode = scenarios meaning that feature files are split into individual scenarios that each have a dedicated runner.

Sometimes it may be desirable, to parallelize complete features. When setting the parallelizationMode = features, only complete features containing all of their source scenarios are generated so each runner runs a complete feature.

Note: For this mode to work, <sourceFeatures> must specify a directory. Also, includeScenarioTags cannot be used.

desiredNumberOfRunners
设置固定 Runners 个数,这样每个 Runner 会运行多个 generated feature 的 cases,也就是每个 Runner 执行的 feature file 个数是变化的。

If you set this options, all generated features will be distributed to a fixed set of runner classes. This means that one runner can potentially run multiple features in sequence.

If this option is not set, its default value is 0 which basically
means “Generate a dedicated runner for every generated feature”.

Note: This cannot be used together with desiredNumberOfFeaturesPerRunner!

desiredNumberOfFeaturesPerRunner
设置每个 Runner 跑多少个 Feature files,根据 feature 的多少,Runner 个数会变动

If you set this option, all generated features will be distributed to a dynamic set of runner classes so that every runner contains a fixed number of generated features. This means that one runner can potentially run multiple features in sequence.

If this option is not set, its default value is 0 which basically
means “Generate a dedicated runner for every generated feature”.

Note: This cannot be used together with desiredNumberOfRunners!

完成了 Cucable 配置,接下来配置并行策略了。相关参数将在后面详细介绍。

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-failsafe-plugin</artifactId><executions><execution><id>Run parallel tests</id><phase>integration-test</phase><goals><goal>integration-test</goal></goals></execution></executions><configuration><testFailureIgnore>true</testFailureIgnore><forkCount>${maven.fork.count}</forkCount><reuseForks>false</reuseForks><argLine>-Dfile.encoding=UTF-8</argLine><disableXmlReport>true</disableXmlReport></configuration></plugin>

缺陷
综上就是有关 Cucable Plugin的介绍,看着挺完美的,但存在不少缺点。

Scenario Level 缺陷
我尝试了,如果不设置 desiredNumberOfRunners 或 desiredNumberOfFeaturesPerRunner 的值,就一个 Scenario 一个 Runner,就算把 forkCount 设置为4,reuseForks 设置为 ture,也能将 Memory 和 CPU 撑爆,跟串行几乎没啥基本,主要是 Runner 太多,4个 Java™ Platform SE binary 进程每跑完一个 Scenario Runner 就得切换执行其它 Scenario Runner,太频繁了,开销太大。

有兴趣的可以尝试设置 desiredNumberOfRunners 或 desiredNumberOfFeaturesPerRunner 的值,我尝试了一下,forkcount 为 10, reusefork 为 true,只看到 4 个 Java 进程,然而还没啥效果呢。

Feature Level缺陷
一个 feature file 生成一个 Runner,这样可以克服上面 Scenario Level 的缺陷,可以减少Java™ Platform SE binary进程切换频率,减少开销。但是这种模式也有缺陷,就是不支持 Tag filter。由于产品历史原因,我们大量 feature file 有包含其它国外团队的Scenaios,而我们只跑 APAC 范围的 Scenarios,所以哇凉哇凉的,但是执着的我怎么可以放弃的,哈哈!

方案四
cucumber-jvm-parallel-plugin
停止维护了,但它是我的最佳方案,哈哈!!

As of cucumber-jvm:4.0.0 parallel execution is supported natively by cucumber. As such, upgrading to Cucumber 4.0.0 is recommended and this plugin is no longer maintained.

支持 Scenario Level 和 Feature Level

This plugin automatically generates a Cucumber JUnit or TestNG runner for each scenario/feature file found in your project.

配置如下, 我是采用 Feature Level 的模式,ParallelScheme->FEATURE, 支持 tag filter,克服了方案三的 Feature Level 的缺陷

<plugin><groupId>com.github.temyers</groupId><artifactId>cucumber-jvm-parallel-plugin</artifactId><version>5.0.0</version><executions><execution><id>generateRunners</id><phase>generate-test-sources</phase><goals><goal>generateRunners</goal></goals><configuration><!-- Mandatory --><!-- List of package names to scan for glue code. --><glue><package>com.company.infra.services.stepdefs</package></glue><featuresDirectory>src/test/resources/features</featuresDirectory><!-- These are optional, with the default values --><!-- The naming scheme to use for the generated test classes.  One of ['simple', 'feature-title', 'pattern'] --><namingScheme>feature-title</namingScheme><!-- One of [SCENARIO, FEATURE]. SCENARIO generates one runner per scenario.  FEATURE generates a runner per feature. --><parallelScheme>FEATURE</parallelScheme><!-- List of cucumber plugins. When none are provided the json formatter is used. For more advanced usage see section about configuring cucumber plugins --><plugins><plugin><name>json</name><extension>json</extension><outputDirectory>${project.build.directory}/cucumber-parallel/json</outputDirectory></plugin><plugin><name>html</name><extension>html</extension><outputDirectory>${project.build.directory}/cucumber-parallel/html</outputDirectory></plugin><plugin><name>rerun</name><extension>rerun</extension><outputDirectory>${project.build.directory}/rerun</outputDirectory></plugin></plugins></configuration></execution></executions></plugin>

featuresDirectory parameter
feature 文件的根目录

The plugin will search featuresDirectory for *.feature files and generate a JUnit test for each one.
WARNING: featuresDirectory must denote a directory within the root of the classpath.
Example:
Resources in src/test/resources are added to the classpath by default.
src/test/resources/features is in the root of the classpath, so would be valid for featuresDirectory
src/test/resources/features/sub_folder is not in the root of the classpath, so would not be valid to put in featuresDirectory

Naming Scheme parameter
配置自动生成文件的命名规则

The naming scheme used for the generated files is controlled by the namingScheme property. The following values are supported:

PropertyGenerated Name
simpleParallelXXIT.java, where XX is a one up counter.
feature-titleThe name is generated based on the feature title with a set of rules to ensure it is a valid classname. The reules are detailed in the next subsection below.
patternGenerate the filename based on the namingPattern property.

By default, generated test files use the simple naming strategy.

执行策略

确定了方案,就得采用最优策略执行。
Fork Options and Parallel Test Execution

两种方式:

1. parallel parameter
控制并发执行的对象,注意这种方式是在一个进程中执行多个线程。

For JUnit 4.7 and onwards, this may be methods, classes, both, suites, suitesAndClasses, suitesAndMethods, classesAndMethods or all.

The important thing to remember with the parallel option is: the concurrency happens within the same JVM process. That is efficient in terms of memory and execution time

线程数配置
useUnlimitedThreads 为 true,不限制线程数。useUnlimitedThreads 为 false 时可以使用 threadCount 和 perCoreThreadCount 参数。还可以通过 threadCountSuites,threadCountClasses,threadCountMethods 在不同粒度限制线程。parallelTestsTimeoutInSeconds 和 parallelTestsTimeoutForcedInSeconds 参数设置线程的超时时间。

The parameter useUnlimitedThreads allows for an unlimited number of threads. Unless useUnlimitedThreads=true, the parameter threadCount can be used with the optional parameter perCoreThreadCount=true (true by default). The parameters useUnlimitedThreads and threadCount are to be interpreted in the context of the value specified for the parallel parameter.

2. Forked Test Execution
创建多个测试进程,如果 forkCount 参数值后加 C,表示乘以 CPU 核数

The parameter forkCount defines the maximum number of JVM processes that maven-surefire-plugin will spawn concurrently to execute the tests. It supports the same syntax as -T in maven-core:
if you terminate the value with a ‘C’, that value will be multiplied with the number of available CPU cores in your system.
For example
forkCount=2.5C on a Quad-Core system will result in forking up to ten concurrent JVM processes that execute tests.

reuseForks 表示一个测试进程执行完了之后是杀掉还是重用来继续执行后续的测试。

The parameter reuseForks is used to define whether to terminate the spawned process after one test class and to create a new process for the next test in line (reuseForks=false), or whether to reuse the processes to execute the next tests (reuseForks=true).

Combining forkCount and parallel
多种并行方式组合

forkCount=0, 或 forkCount=1/reuseForks=true,可以和 parallel 自由组合。

The modes forkCount=0 and forkCount=1/reuseForks=true can be combined freely with the available settings for parallel.

forkCount 的测试进程是按类为单位执行的,测试类整个整个的传到测试进程中执行。reuseForks=false 或 forkCount>1 时,就会使用独立的测试进程,所以 parallel=classes就失效了。但是还是可以组合 parallel=methods/threadCount=n 指定每个测试进程里的并发线程数。

As reuseForks=false creates a new JVM process for each test class, using parallel=classes would have no effect. You can still use parallel=methods, though.

When using reuseForks=true and a forkCount value larger than one, test classes are handed over to the forked process one-by-one. Thus, parallel=classes would not change anything. However, you can use parallel=methods: classes are executed in forkCount concurrent processes, each of the processes can then use threadCount threads to execute the methods of one class in parallel.

parellel 限制了类不能并发,但在 forkCount 并发进程中可以执行类,然后每个进程可以使用threadCount线程并行执行一个类的方法。这句话说明了 parallel 只能限制在 jvm 内部是并发执行方法或者类,说白是限制线程,而对进程没有约束。parallel 和 forkcount 分别是线程和进程级别。

本地Jenkins执行结果

Smoke Test Cases:
feature Files: 84
Scenarios: 596

方式执行时间说明
普通串行20min
Cucable-plugin30minScenario level paralle run,forkcout=10,reusefork=true, desiredNumberOfRunners=84
Cucable-plugin37minScenario level paralle run,forkcout=10,reusefork=true, desiredNumberOfRunners=0
Cucable-pluginNAFeature level paralle run,因为 不支持 tag filter 所以没必要验证
cucumber-jvm-parallel-plugin5-6minFeature level praralle run,forkcout=10,reusefork=true 最佳方案建议将 reusefork 设为 false,请看下面的补充更新
cucumber-jvm-parallel-plugin5-6minFeature level praralle run,forkcout=2.5C,reusefork=true 本机 4 核,但是居然起了 20 个 java 进程
cucumber-jvm-parallel-plugin大量 case 失败,多线程不安全Feature level praralle run,parallel=classes,useUnlimitedThreads=true
cucumber-jvm-parallel-plugin大量 case 失败,多线程不安全Feature level praralle run,parallel=classesAndMethods,useUnlimitedThreads=true
cucumber-jvm-parallel-plugin52min 震惊,尽管没有因为多线程失败Feature level praralle run,parallel=methods,useUnlimitedThreads=true
cucumber-jvm-parallel-plugin7minFeature level praralle run,Combining forkcout=10,reusefork=true,parallel=methods,useUnlimitedThreads=true,

Regresion Test Cases:
feature Files: 84
Scenarios: 9852

方式执行时间说明
普通串行4h
cucumber-jvm-parallel-plugin1h20minFeature level praralle run,forkcout=10,reusefork=true

补充更新 - 2020-10-24

经过一周在 remote jenkins 上的运行情况观察,这种方案 forkcout=10, reusefork=true 经常出现下列异常,Runner 进程异常退出,导致一个 feature cases 执行结束后没法正常将执行结果写入 cucumber report 的 json文件,也就不能正常生成 report 了

org.apache.maven.surefire.booter.SurefireBooterForkException: ExecutionException The forked VM terminated without properly saying goodbye. VM crash or System.exit called?

导致上面异常的原因,fork 的 java 进程由于是 reuse 模式,随着不断执行太多 feature cases,内存一直累积攀升,没能及时释放无效内存,同时 10 个 java 进程都如此,VM 性能可想而知了。设置reusefork=false,观察 java 进程的内存占用一直维持在一个稳定的数字,没有累积变高的现象,就不会出现这种异常了。

详细配置:

Cucumber-JVM-Parallel-Plugin:
mvn clean test -P p-parallel cluecumber-report:reporting -Dcucumber.options=“–tags @smoke”

<profile><id>p-parallel</id><activation><activeByDefault>false</activeByDefault></activation><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><groupId>com.github.temyers</groupId><artifactId>cucumber-jvm-parallel-plugin</artifactId><version>5.0.0</version><executions><execution><id>generateRunners</id><phase>generate-test-sources</phase><goals><goal>generateRunners</goal></goals><configuration><!-- Mandatory --><!-- List of package names to scan for glue code. --><glue><package>com.company.infra.services.stepdefs</package></glue><featuresDirectory>src/test/resources/features</featuresDirectory><!-- These are optional, with the default values --><!-- The naming scheme to use for the generated test classes.  One of ['simple', 'feature-title', 'pattern'] --><namingScheme>feature-title</namingScheme><!-- One of [SCENARIO, FEATURE]. SCENARIO generates one runner per scenario.  FEATURE generates a runner per feature. --><parallelScheme>FEATURE</parallelScheme><!-- List of cucumber plugins. When none are provided the json formatter is used. For more advanced usage see section about configuring cucumber plugins --><plugins><plugin><name>json</name><extension>json</extension><outputDirectory>${project.build.directory}/cucumber-parallel/json</outputDirectory></plugin><plugin><name>html</name><extension>html</extension><outputDirectory>${project.build.directory}/cucumber-parallel/html</outputDirectory></plugin><plugin><name>rerun</name><extension>rerun</extension><outputDirectory>${project.build.directory}/rerun</outputDirectory></plugin></plugins></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.0.0-M3</version><configuration><argLine>-Dfile.encoding=UTF-8</argLine><testFailureIgnore>true</testFailureIgnore><!--<forkCount>2.5C</forkCount>--><!--<reuseForks>true</reuseForks>--><parallel>classesAndMethods</parallel ><useUnlimitedThreads>true</useUnlimitedThreads><includes><include>**/*IT.class</include></includes></configuration></plugin><plugin><groupId>com.trivago.rta</groupId><artifactId>cluecumber-report-plugin</artifactId><version>2.5.0</version><executions><execution><id>report</id><phase>post-integration-test</phase><goals><goal>reporting</goal></goals></execution></executions><configuration><startPage>ALL_FEATURES</startPage><expandBeforeAfterHooks>false</expandBeforeAfterHooks><expandStepHooks>false</expandStepHooks><expandDocStrings>true</expandDocStrings><customParameters><Cucuzzi>Infra Shared Service Tests Report</Cucuzzi></customParameters><sourceJsonReportDirectory>${project.build.directory}/cucumber-parallel/json</sourceJsonReportDirectory><generatedHtmlReportDirectory>${project.build.directory}/cluecumber-report</generatedHtmlReportDirectory></configuration></plugin></plugins></build></profile>

cucable plugin:
mvn clean verify failsafe:verify -Pparallel -Dist=“@smoke”

<profile><id>parallel</id><activation><activeByDefault>false</activeByDefault></activation><properties><ist>@ist</ist></properties><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.0.0-M3</version><configuration><argLine>-Dfile.encoding=UTF-8</argLine><testFailureIgnore>false</testFailureIgnore><skipTests>true</skipTests></configuration></plugin><plugin><groupId>com.trivago.rta</groupId><artifactId>cucable-plugin</artifactId><version>1.5.1</version><executions><execution><id>generate-test-resources</id><phase>generate-test-resources</phase><goals><goal>parallel</goal></goals></execution></executions><configuration><!-- Required properties --><sourceRunnerTemplateFile>src/test/java/parallel/CucableJavaTemplate.java</sourceRunnerTemplateFile><sourceFeatures>src/test/resources/features</sourceFeatures><generatedFeatureDirectory>${project.build.directory}/parallel/features</generatedFeatureDirectory><generatedRunnerDirectory>${project.build.directory}/parallel/runners</generatedRunnerDirectory><!-- Optional properties --><numberOfTestRuns>1</numberOfTestRuns><includeScenarioTags>${ist}</includeScenarioTags><logLevel>default</logLevel><desiredNumberOfRunners/></configuration></plugin><plugin><groupId>com.trivago.rta</groupId><artifactId>cluecumber-report-plugin</artifactId><version>1.10.2</version><executions><execution><id>report</id><phase>post-integration-test</phase><goals><goal>reporting</goal></goals></execution></executions><configuration><expandBeforeAfterHooks>false</expandBeforeAfterHooks><expandStepHooks>false</expandStepHooks><expandDocStrings>true</expandDocStrings><customParameters><Cucuzzi>Infra Shared Service Tests Report</Cucuzzi></customParameters><sourceJsonReportDirectory>${project.build.directory}/cucumber-report</sourceJsonReportDirectory><generatedHtmlReportDirectory>${project.build.directory}/test-report</generatedHtmlReportDirectory></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-failsafe-plugin</artifactId><version>2.18</version><executions><execution><id>Run parallel tests</id><phase>integration-test</phase><goals><goal>integration-test</goal></goals></execution></executions><configuration><testFailureIgnore>false</testFailureIgnore><forkCount>15</forkCount><reuseForks>false</reuseForks><argLine>-Dfile.encoding=UTF-8</argLine><disableXmlReport>false</disableXmlReport></configuration></plugin><plugin><groupId>org.codehaus.mojo</groupId><artifactId>build-helper-maven-plugin</artifactId><version>1.12</version><executions><execution><id>add-test-source</id><phase>generate-test-sources</phase><goals><goal>add-test-source</goal></goals><configuration><sources><source>${project.build.directory}/parallel/runners</source></sources></configuration></execution></executions></plugin></plugins></build>
</profile>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1621324.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Cucumber框架入门篇

Cucumber介绍 Cucumber是一个支持BDD&#xff08;Behavior Driven Development&#xff09;,即行为驱动开发的自动化测试框架。在进行单元测试或者集成测试之前&#xff0c;事先将测试的步骤和验证信息用通用的语言&#xff08;英语&#xff09;定义好&#xff0c;使得测试的步…

表和Json的相互操作

目录 一、表转Json 1.使用 for json path 2.如何返回单个Json 3.如何给返回的Json增加一个根节点呢 4.如何给返回的Json增加上一个节点 二、对Json基本操作 1.判断给的字符串是否是Json格式 2.从 JSON 字符串中提取标量值 3. 从 JSON 字符串中提取对象或数组 4. 更…

Linux内核的动态电压和电流控制接口(regulatordvfs)

Linux内核的动态电压和电流控制接口 前面已经提到半导体器件的功耗是两个部分组成&#xff0c;一是静态功耗&#xff0c;一是动态功耗。静态功耗主要来自待机状态的泄漏电流&#xff0c;相比而言动态功耗更大&#xff0c;例如&#xff0c;音视频播放中频率和电压的增加会让电量…

时序预测 | MATLAB实现SSA-XGBoost(麻雀算法优化极限梯度提升树)时间序列预测

时序预测 | MATLAB实现SSA-XGBoost(麻雀算法优化极限梯度提升树)时间序列预测 目录 时序预测 | MATLAB实现SSA-XGBoost(麻雀算法优化极限梯度提升树)时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实现SSA-XGBoost时间序列预测&#xff0c;麻…

字节码操作的手术刀-Javassist

Javassist 前面文章介绍的 ASM 入门门槛还是挺高的&#xff0c;需要跟底层的字节码指令打交道&#xff0c;优点是小巧、性能好。Javassist 是一个性能比 ASM 稍差但是使用起来简单很多的字节码操作库&#xff0c;不需要了解字节码指令&#xff0c;由东京工业大学的数学和计算机…

Unittest 笔记:unittest拓展生成HTM报告

HTMLTestRunner 是一个unitest拓展可以生成HTML 报告 下载地址&#xff1a;GitHub: https://github.com/defnnig/HTMLTestRunner HTMLTestRunner是一个独立的py文件&#xff0c;可以放在Lib 作为第三方模块使用或者作为项目的一部分。 方式1&#xff1a; 验证是否安装成功&…

TSC TTP-244条码打印机如何批量打印二维码

二维码的应用可以说是非常的普遍了&#xff0c;二维码在应用之前不但需要条码打印机批量打印二维码&#xff0c;还需要相关的二维码制作软件制作二维码。今天小编就教大家用TSC TTP-244条码打印机批量打印二维码。 1、打开二维码制作软件&#xff0c;新建一个标签&#xff0c;选…

条码打印机如何打印流水号

流水号现在用途也是非常广泛的&#xff0c;应用于各行各业&#xff0c;今天小编就教大家如何用条码打印机打印流水号&#xff0c;操作也是非常简单&#xff0c;先用条码打印软件生成流水号&#xff0c;然后连接条码打印机打印流水号。 打开条码打印软件&#xff0c;新建标签&a…

条码打印软件如何连接激光打印机打印条码标签

在连接打印机打印条码标签之前&#xff0c;需要对条码打印软件有一个简单的了解&#xff0c;条码打印软件是通过驱动来连接各种打印机进行打印条码标签的&#xff0c;所以在连接激光打印机打印条码标签时&#xff0c;需要在电脑上安装通用激光打印机驱动。接下来我们看看过程。…

反转链表+交换两个链表的节点

目录 ​编辑 一&#xff0c;反转链表 1.题目描述 2.例子 3.题目接口 4.分析以及解题代码 1.迭代法 2.递归写法 二&#xff0c;两两交换两个链表中的节点 1.题目描述 2.例子 3.题目接口 4.题目分析以及解法 一&#xff0c;反转链表 1.题目描述 首先来看看反转链表的…

86. 分隔链表(中等系列)

给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x 3 输出&…

hiredis的安装与使用

hiredis的介绍 Hiredis 是一个用于 C 语言的轻量级、高性能的 Redis 客户端库。它提供了一组简单易用的 API&#xff0c;用于与 Redis 数据库进行交互。Hiredis 支持 Redis 的所有主要功能&#xff0c;包括字符串、哈希、列表、集合、有序集合等数据结构的读写操作&#xff0c…

Docker vs. Podman: 选择容器技术的智慧之选

嗨&#xff0c;各位亲爱的程序员小伙伴们&#xff01;当我们步入容器技术的世界&#xff0c;往往会在众多选择中迷茫。两个备受瞩目的容器工具&#xff0c;Docker 和 Podman&#xff0c;都在业界掀起了一股风潮。今天&#xff0c;我将带你深入探索&#xff0c;为什么在 Docker …

购买的gmail谷歌邮箱,faceboolhotmail邮箱mail邮箱yahoo,aol在国外使用完全不受影响,购买地址推荐:

购买的谷歌邮箱,faceboolhotmail邮箱mail邮箱yahoo,aol在国外使用完全不受影响&#xff0c;购买地址推荐&#xff1a;邮箱谷歌批发购买地址&#xff1a;buyemail.buyaccountemail.com记好了 登录方法如下 1、下载QQ邮箱手机客户端 2、先使用QQ邮箱登陆到客户端 谷歌邮箱 …

免费激活Yahoo邮箱的POP3服务

通过POP3&#xff0c;我们就能够在本机上使用各种邮件客户端软件(Foxmail、Outlook等)收发电子邮件。 Yahoo免费邮箱没有提供免费POP3服务&#xff0c;而通过邮箱里的设置激活该服务时则被提示需要收费。如图1所示 图1 笔者就给大伙介绍一个小技巧&#xff0c;可以免费地打开Ya…

Foxmail6下@yahoo.cn邮箱设置

http://www.88sina.com/foxmail-yahoo.cn/(转) 昨天申请了一个yahoo.cn的邮箱&#xff0c;在Foxmail中弄了半天&#xff0c;就是使用不了&#xff0c;不是提示输密码就是提示这样那样的错误&#xff0c;今天在网上找来找去&#xff0c;试来试去&#xff0c;终于可以正常收发邮件…

雅虎邮箱 找回密码_如何恢复被遗忘的Yahoo! 密码

雅虎邮箱 找回密码 If you don’t use a password manager, those complex passwords can be pretty hard to remember. If you’ve forgotten your Yahoo password, you can’t really recover that same password, but it’s easy enough to recover your account by resetti…

类似于yahoo邮箱登陆的提示效果

当鼠标聚焦到邮箱地址文本框时&#xff0c;文本框内的“请输入邮箱地址”文字被清空。 效果图&#xff1a; <% Page Language"C#" AutoEventWireup"true" CodeFile"类似于yahoo邮箱登陆的提示效果.aspx.cs" Inherits"类似于yahoo邮箱登…

java雅虎邮件发送

java雅虎邮件发送 1、在网页上登录雅虎邮箱-需翻墙2、登录成功后台&#xff0c;进入账号资料3、进入账户安全&#xff0c;开启双重验证4、创建应用5、替换配置中的邮箱密码即可使用 申请雅虎邮箱后&#xff1a; application.yml配置 spring:mail:host: smtp.mail.yahoo.compo…