为什么 Maven Surefire 3 无法使用 Docker 在 Jenkins 中 运行 我的 Cucumber 测试?
Why does Maven Surefire 3 fail to run my Cucumber test in Jenkins with Docker?
我有一个 Java 项目,其中包含一些 Cucumber 测试和一些常规 JUnit 测试,由 Maven 管理。
我想 运行 在 Jenkins 中使用 Docker 进行测试,所以我写了这个 Jenkinsfile
:
pipeline {
agent {
docker {
image 'maven:3.6.1'
}
}
stages {
stage('build') {
steps {
sh 'mvn clean verify -Dmaven.test.failure.ignore=true'
}
post {
success {
junit 'target/surefire-reports/**/*.xml'
}
}
}
}
}
当我 运行 构建时,常规测试通过但 Cucumber 测试失败:
Error Message
URI has a query component
Stacktrace
java.lang.IllegalArgumentException: URI has a query component
直到我为 maven-surefire-plugin
禁用 trimStackTrace
时,我才得到详细信息:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
java.lang.IllegalArgumentException: URI has a query component
at java.base/java.io.File.<init>(File.java:427)
at cucumber.runtime.io.ZipResourceIterator.<init>(ZipResourceIterator.java:22)
at cucumber.runtime.io.ZipResourceIteratorFactory.createIterator(ZipResourceIteratorFactory.java:24)
at cucumber.runtime.io.ZipThenFileResourceIteratorFactory.createIterator(ZipThenFileResourceIteratorFactory.java:22)
at cucumber.runtime.io.DelegatingResourceIteratorFactory.createIterator(DelegatingResourceIteratorFactory.java:49)
at cucumber.runtime.io.ClasspathResourceIterable.iterator(ClasspathResourceIterable.java:35)
at cucumber.runtime.io.ResourceLoaderClassFinder.getDescendants(ResourceLoaderClassFinder.java:25)
at cucumber.runtime.Reflections.instantiateSubclasses(Reflections.java:34)
at cucumber.runtime.BackendModuleBackendSupplier.loadBackends(BackendModuleBackendSupplier.java:52)
at cucumber.runtime.BackendModuleBackendSupplier.get(BackendModuleBackendSupplier.java:39)
at cucumber.runner.ThreadLocalRunnerSupplier.createRunner(ThreadLocalRunnerSupplier.java:42)
at cucumber.runner.ThreadLocalRunnerSupplier.access[=14=]0(ThreadLocalRunnerSupplier.java:13)
at cucumber.runner.ThreadLocalRunnerSupplier.initialValue(ThreadLocalRunnerSupplier.java:22)
at cucumber.runner.ThreadLocalRunnerSupplier.initialValue(ThreadLocalRunnerSupplier.java:19)
at java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:195)
at java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
at cucumber.runner.ThreadLocalRunnerSupplier.get(ThreadLocalRunnerSupplier.java:38)
at cucumber.api.junit.Cucumber$RunCucumber.evaluate(Cucumber.java:146)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:365)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:273)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:159)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418)
这很令人费解,因为:
- 当 运行在本地
mvn verify
时,它有效
- 当在
Dockerfile
中使用 agent any
而不是 Maven Docker 图像时,它起作用了
- 当使用
maven-surefire-plugin
版本 2.22.2
而不是 3.0.0-M3
时,它有效
我以 Jenkins 身份登录并查看了工作区:
$ sudo su - jenkins
$ ls /var/lib/jenkins/workspace/question-mark-dir_master
'?' Jenkinsfile LICENSE pom.xml README.md src target
那里有一个问号 (?
) 目录,它包含 .m2
目录,以及项目所需的所有工件。
我怀疑这可能是异常的原因,因为类路径中有问号,问号是引入URI查询组件的。
目前我可以降级到 maven-surefire-plugin
版本 2.22.2
作为解决方法。
但这里的实际问题是什么?
- 会不会是新
maven-surefire-plugin
中的错误?从堆栈跟踪来看,我不会打赌。
- 是 Jenkins 还是 Maven docker 映像(哪一个?)中的问题导致目录具有如此不幸的名称?
- 我是不是做错了什么?
如果你想试试这个,我有 a MVCE。
在我的本地 Jenkins 安装中,我创建了一个类型为 "Multibranch Pipeline" 的新项目,并在 "Branch Sources" 中添加了我的本地 git 项目作为 git 项目存储库。没有其他变化。
通过结合 TYY 的 comment to the question and the documentation of the maven docker image,我能够以一种避免创建问号目录的方式配置 Jenkinsfile
。
我们需要通知 Maven 用户的主目录,并将其映射到容器外的目录。
pipeline {
agent {
docker {
image 'maven:3.6.1'
args '-v /var/lib/jenkins:/usr/src/mymaven -w /usr/src/mymaven'
}
}
stages {
stage('build') {
steps {
sh 'MAVEN_OPTS="-Duser.home=/usr/src/mymaven" mvn clean verify -Dmaven.test.failure.ignore=true'
}
// ...
}
}
}
-v /var/lib/jenkins:/usr/src/mymaven
将主机上的/var/lib/jenkins
目录映射到容器 内的/usr/src/mymaven
目录
-w /usr/src/mymaven
设置容器内的工作目录
MAVEN_OPTS="-Duser.home=/usr/src/mymaven"
为 Maven 设置 user.home
Java 属性
在 MCVE 的 a branch 上证明它有效。
我有一个 Java 项目,其中包含一些 Cucumber 测试和一些常规 JUnit 测试,由 Maven 管理。
我想 运行 在 Jenkins 中使用 Docker 进行测试,所以我写了这个 Jenkinsfile
:
pipeline {
agent {
docker {
image 'maven:3.6.1'
}
}
stages {
stage('build') {
steps {
sh 'mvn clean verify -Dmaven.test.failure.ignore=true'
}
post {
success {
junit 'target/surefire-reports/**/*.xml'
}
}
}
}
}
当我 运行 构建时,常规测试通过但 Cucumber 测试失败:
Error Message
URI has a query component
Stacktrace
java.lang.IllegalArgumentException: URI has a query component
直到我为 maven-surefire-plugin
禁用 trimStackTrace
时,我才得到详细信息:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
java.lang.IllegalArgumentException: URI has a query component
at java.base/java.io.File.<init>(File.java:427)
at cucumber.runtime.io.ZipResourceIterator.<init>(ZipResourceIterator.java:22)
at cucumber.runtime.io.ZipResourceIteratorFactory.createIterator(ZipResourceIteratorFactory.java:24)
at cucumber.runtime.io.ZipThenFileResourceIteratorFactory.createIterator(ZipThenFileResourceIteratorFactory.java:22)
at cucumber.runtime.io.DelegatingResourceIteratorFactory.createIterator(DelegatingResourceIteratorFactory.java:49)
at cucumber.runtime.io.ClasspathResourceIterable.iterator(ClasspathResourceIterable.java:35)
at cucumber.runtime.io.ResourceLoaderClassFinder.getDescendants(ResourceLoaderClassFinder.java:25)
at cucumber.runtime.Reflections.instantiateSubclasses(Reflections.java:34)
at cucumber.runtime.BackendModuleBackendSupplier.loadBackends(BackendModuleBackendSupplier.java:52)
at cucumber.runtime.BackendModuleBackendSupplier.get(BackendModuleBackendSupplier.java:39)
at cucumber.runner.ThreadLocalRunnerSupplier.createRunner(ThreadLocalRunnerSupplier.java:42)
at cucumber.runner.ThreadLocalRunnerSupplier.access[=14=]0(ThreadLocalRunnerSupplier.java:13)
at cucumber.runner.ThreadLocalRunnerSupplier.initialValue(ThreadLocalRunnerSupplier.java:22)
at cucumber.runner.ThreadLocalRunnerSupplier.initialValue(ThreadLocalRunnerSupplier.java:19)
at java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:195)
at java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
at cucumber.runner.ThreadLocalRunnerSupplier.get(ThreadLocalRunnerSupplier.java:38)
at cucumber.api.junit.Cucumber$RunCucumber.evaluate(Cucumber.java:146)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:365)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:273)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:159)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418)
这很令人费解,因为:
- 当 运行在本地
mvn verify
时,它有效 - 当在
Dockerfile
中使用agent any
而不是 Maven Docker 图像时,它起作用了 - 当使用
maven-surefire-plugin
版本2.22.2
而不是3.0.0-M3
时,它有效
我以 Jenkins 身份登录并查看了工作区:
$ sudo su - jenkins
$ ls /var/lib/jenkins/workspace/question-mark-dir_master
'?' Jenkinsfile LICENSE pom.xml README.md src target
那里有一个问号 (?
) 目录,它包含 .m2
目录,以及项目所需的所有工件。
我怀疑这可能是异常的原因,因为类路径中有问号,问号是引入URI查询组件的。
目前我可以降级到 maven-surefire-plugin
版本 2.22.2
作为解决方法。
但这里的实际问题是什么?
- 会不会是新
maven-surefire-plugin
中的错误?从堆栈跟踪来看,我不会打赌。 - 是 Jenkins 还是 Maven docker 映像(哪一个?)中的问题导致目录具有如此不幸的名称?
- 我是不是做错了什么?
如果你想试试这个,我有 a MVCE。 在我的本地 Jenkins 安装中,我创建了一个类型为 "Multibranch Pipeline" 的新项目,并在 "Branch Sources" 中添加了我的本地 git 项目作为 git 项目存储库。没有其他变化。
通过结合 TYY 的 comment to the question and the documentation of the maven docker image,我能够以一种避免创建问号目录的方式配置 Jenkinsfile
。
我们需要通知 Maven 用户的主目录,并将其映射到容器外的目录。
pipeline {
agent {
docker {
image 'maven:3.6.1'
args '-v /var/lib/jenkins:/usr/src/mymaven -w /usr/src/mymaven'
}
}
stages {
stage('build') {
steps {
sh 'MAVEN_OPTS="-Duser.home=/usr/src/mymaven" mvn clean verify -Dmaven.test.failure.ignore=true'
}
// ...
}
}
}
-v /var/lib/jenkins:/usr/src/mymaven
将主机上的/var/lib/jenkins
目录映射到容器 内的-w /usr/src/mymaven
设置容器内的工作目录MAVEN_OPTS="-Duser.home=/usr/src/mymaven"
为 Maven 设置
/usr/src/mymaven
目录
user.home
Java 属性
在 MCVE 的 a branch 上证明它有效。