In the gradle script below, maxParallelForks
and forkEvery
will start a new process with a new JVM per Junit test class. Is there a way to give each test process/JVM/Junit class its own unique system property?
I tried an experiment where I passed a UUID value as a system property in the beforeSuite
method.
apply plugin:'java'
test {
ignoreFailures = true
maxParallelForks = 8
forkEvery = 1
systemProperty 'some.prop', 'value'
beforeSuite { descriptor ->
logger.lifecycle("Before suite: " + descriptor)
def theuuid = UUID.randomUUID().toString()
println "beforeSuite uuid =" + theuuid
systemProperty 'some.uuid', theuuid
}
beforeTest { descriptor ->
logger.lifecycle("Running test: " + descriptor)
}
}
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.12'
}
The JUnit test classes, LoginTest1
and LoginTest2
print out the system property in a test method.
public class LoginTest1 {
private String userName="Gradle";
@Test
public void testUserName() throws Exception {
System.out.println("LoginTest1: Testing Login ");
System.out.println("LoginTest1: Testing Login some.prop -> "
+ System.getProperty("some.prop"));
System.out.println("LoginTest1: Testing Login some.uuid -> "
+ System.getProperty("some.uuid"));
assertTrue(userName.equals("Gradle"));
}
}
public class LoginTest2 {
private String userName="Gradle";
@Test
public void testUserName() throws Exception {
System.out.println("LoginTest2: Testing Login ");
System.out.println("LoginTest2: Testing Login some.prop -> "
+ System.getProperty("some.prop"));
System.out.println("LoginTest2: Testing Login some.uuid -> "
+ System.getProperty("some.uuid"));
assertTrue(userName.equals("Gradle"));
}
}
As you can see from the gradle output below, the beforeSuite
method is called before running individual JUnit test methods. Both test classes get the same system property of 6a006689-474f-47d5-9649-a88c92b45095
$ gradle clean test
> Task :test
Before suite: Gradle Test Run :test
beforeSuite uuid =6a006689-474f-47d5-9649-a88c92b45095
Before suite: Gradle Test Executor 1
beforeSuite uuid =39edb608-3027-4ad8-bd15-f52f50175582
Before suite: Test class ch6.login.LoginTest1
beforeSuite uuid =c0ce55e0-924c-4319-becb-bc27229c9cf3
Before suite: Gradle Test Executor 2
beforeSuite uuid =c15eaac0-7ea7-46d2-8235-8dc49b3f7a39
Running test: Test testUserNameNotNull(ch6.login.LoginTest1)
Before suite: Test class ch6.login.LoginTest2
beforeSuite uuid =73abbe24-5b78-4935-867a-445ac4ac20ef
Running test: Test testUserName(ch6.login.LoginTest1)
Running test: Test testUserName(ch6.login.LoginTest2)
JUnit test class output:
LoginTest1: Testing Login
LoginTest1: Testing Login some.prop -> value
LoginTest1: Testing Login some.uuid -> 6a006689-474f-47d5-9649-a88c92b45095
LoginTest2: Testing Login
LoginTest2: Testing Login some.prop -> value
LoginTest2: Testing Login some.uuid -> 6a006689-474f-47d5-9649-a88c92b45095
It looks like there's an initial call to beforeSuite (seen as ":test") and then once per Test Executor. The first uuid value ("6a006689") is set across all JVMs. Is there anything like a simple beforeClass method I could use?
In the debug log below,it looks like the Test Executors are given the jvm argument -Dsome.uuid=6a006689-474f-47d5-9649-a88c92b45095
and that is used soley to create new JVMs (as per the forkEvery
config). This makes me think that what I want to do is impossible in Gradle.
20:23:42.466 [INFO] [org.gradle.process.internal.DefaultExecHandle] Starting process 'Gradle Test Executor 1'. Working directory: /a/b/ Command: /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djava.security.manager=worker.org.gradle.process.internal.worker.child.BootstrapSecurityManager -Dorg.g
radle.native=false -Dsome.prop=value -Dsome.uuid=6a006689-474f-47d5-9649-a88c92b45095 -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -ea -cp /data/.gradle/caches/4.3.1/workerMain/gradle-worker.jar worker.org.gradle.process.internal.worker.GradleWorkerMain 'Gradle Test Executor 1'
20:23:42.466 [INFO] [org.gradle.process.internal.DefaultExecHandle] Starting process 'Gradle Test Executor 2'. Working directory: /a/b/ Command: /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djava.security.manager=worker.org.gradle.process.internal.worker.child.BootstrapSecurityManager -Dorg.g
radle.native=false -Dsome.prop=value -Dsome.uuid=6a006689-474f-47d5-9649-a88c92b45095 -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -ea -cp /data/.gradle/caches/4.3.1/workerMain/gradle-worker.jar worker.org.gradle.process.internal.worker.GradleWorkerMain 'Gradle Test Executor 2'
Update 8/2018
The following solution does not work in Gradle 4.9 and greater
Instead, there is a org.gradle.test.worker system property which you can use to distinguish between workers, calculate display names, temporary directories, etc. This test.worker is simply an increasing long value. The Gradle team has no plans on making Test Workers configurable. A pity because I had to do some major refactoring in my test code in order to upgrade Gradle. The "@BeforeAllCallback" in JUnit 5 might be useful to you.
Trying to solve by intercepting Test.copyTo(JavaForkOptions target)
This code demonstrates lance-java 's suggestion and succeeds in answering my question. I have the following build.gradle script which uses MyTestTask to extend Test and add a unique value called my_uuid
. Note that the build script must have forkEvery = 1 to ensure copyTo
is called once per JUnit test:
class MyTestTask extends Test {
public Test copyTo(JavaForkOptions target) {
super.copyTo(target);
String uuid = UUID.randomUUID().toString();
System.out.println("MyTestTask copyTo:\n my_uuid=" + uuid);
System.out.println("\t before getJvmArgs:" + target.getJvmArgs());
System.out.println("\t before getSystemProperties:" + target.getSystemProperties());
target.systemProperty("my_uuid", uuid);
System.out.println("\t after getJvmArgs:" + target.getJvmArgs());
System.out.println("\t after getSystemProperties:" + target.getSystemProperties());
return this;
}
}
///
subprojects {
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.12'
}
test {
enabled false // This doesn't seem to matter.
}
task my_test(type: MyTestTask) {
forkEvery 1 // need this so copyTo called each JUnit test
testLogging.showStandardStreams = true
doFirst {
def uuid = UUID.randomUUID().toString()
println "TEST:Task name is $name. In doFirst. uuid is " + uuid
jvmArgs '-Dcommon_uuid='+ uuid
}
}
}
The Junit tests that are executed are trivial and simply print out System.getProperty("my_uuid") and System.getProperty("common_uuid").
> Task :CustomCopyToSubProject:my_test
TEST:Task name is my_test. In doFirst. uuid is eed1ee92-7805-4b8e-a74c-96218d1be6e1
MyTestTask copyTo:
MyTestTask copyTo:
my_uuid=a7996c20-1085-4397-9e32-fd84467dcb45
my_uuid=69f6f347-71d0-4e5d-9fad-66bf4468fab3
before getJvmArgs:[]
before getJvmArgs:[]
before getSystemProperties:[common_uuid:eed1ee92-7805-4b8e-a74c-96218d1be6e1]
before getSystemProperties:[common_uuid:eed1ee92-7805-4b8e-a74c-96218d1be6e1]
after getJvmArgs:[]
after getJvmArgs:[]
after getSystemProperties:[common_uuid:eed1ee92-7805-4b8e-a74c-96218d1be6e1, my_uuid:69f6f347-71d0-4e5d-9fad-66bf4468fab3]
after getSystemProperties:[common_uuid:eed1ee92-7805-4b8e-a74c-96218d1be6e1, my_uuid:a7996c20-1085-4397-9e32-fd84467dcb45]
ch3.LoginTest2 > testUserName2 STANDARD_OUT
LoginTest2.java: Testing Login ! >>---> my_uuid=a7996c20-1085-4397-9e32-fd84467dcb45 common_uuid=eed1ee92-7805-4b8e-a74c-96218d1be6e1
ch3.LoginTest1 > testUserName1 STANDARD_OUT
LoginTest1.java: Testing Login ! >>---> my_uuid=69f6f347-71d0-4e5d-9fad-66bf4468fab3 common_uuid=eed1ee92-7805-4b8e-a74c-96218d1be6e1
The task my_test
sets common_uuid
in doFirst
. The MyTestTask.copyTo
method sets its own my_uuid
.
You can see from the output and MyTestTask is called twice, once per JUnit test task (LoginTest1 and LoginTest2). The two JUnit tests get their own unique system property in my_uuid
.