'How can I pass tags when starting a cucumber android run using Gradle?
I have a suite of cucumber android tests (they are instrumented tests) and I run them using gradlew connectedCheck
. What I'm trying to do is run only certain tags when I run my tests (that's what tags are for, right?) using command-line options, without having to modify the code every time between test runs (so I can easily run these from CI server etc).
For example, I have tags @one
and @two
. I want to perform two gradle builds, the first with all the @one
tests, and the second with all the @two
tests. This would be part of an automated pipeline, so I wouldn't be able to modify the code (with the @CucumberOptions
) between these two builds.
Here's what I've tried so far:
Attempt 1: Multiple CucumberOptions
annotated classes
I tried having multiple runners (subclasses of CucumberAndroidJUnitRunner
) and a different @CucumberOptions
on each one, specifying different tags, then controlling which one was used via Gradle properies. I then did two builds, one with each runner. However, in both cases, Cucumber just used the @CucumberOptions
annotation from the first runner alphabetically, so the tags were the same on both test runs. (In other words, even if the runner in use has a @CucumberOptions
annotation, that's not necessarily the one that gets used.)
Attempt 2: Passing system property using gradle -D
option
I found a hint from this thread that I might be able to pass -Dcucumber.options="--tags @two"
but I tried this and it didn't work. It still just took the tags from the @CucumberOptions
and ignored whatever I passed on the command-line. (This thread was confusing anyway as it started off talking about Gradle but later it was talking about Maven. I am using Gradle rather than Maven obviously).
I also tried -Dcucumber.filter.tags="@two"
(which I found from the cucumber-jvm docs, talking about Maven not Gradle) but this also didn't work, the same as above.
Other investigations
- I looked at loads of online guides, but they were all based on either modifying the
@CucumberOptions
or runningcucumber
from the command-line (rather than via gradle) - Obviously Java doesn't let you define annotation values by method calls, even if they are static methods, so the value in
@CucumberOptions
has to be constant (I double checked this) - I could dynamically generate the
@CucumberOptions
-annotated class at runtime, but I couldn't figure out how to ensure that this happened before the cucumber instrumentation runner looked for it
Versions used
androidTestImplementation 'io.cucumber:cucumber-android:4.8.4'
androidTestImplementation 'io.cucumber:cucumber-picocontainer:4.8.1'
Solution 1:[1]
You can pass options through gradle using:
-Pandroid.testInstrumentationRunnerArguments.key=value
For example:
-Pandroid.testInstrumentationRunnerArguments.tags=@two
These options are then passed to instrumentation and handled by cucumber, see https://github.com/cucumber/cucumber-jvm/pull/597
So you can use for example:
gradlew connectedCheck -Pandroid.testInstrumentationRunnerArguments.tags=@two
Solution 2:[2]
For now, I am resorting to handling it myself through JUnit assumptions.
I added a new build config field based on a project property, with default value @main
which is one of my tags.
android {
defaultConfig {
...
buildConfigField "String", "CUCUMBER_TAGS", "\"${getCucumberTags()}\""
}
}
def getCucumberTags() {
project.hasProperty("cucumbertags")
? project.getProperties().get("cucumbertags")
: "@main"
}
I added a new class in my glue package
public class TagConfiguration {
private boolean checkTagsMatch(final Scenario scenario) {
// we check if at least one of the configured tags is present for this scenario
// this is equivalent to Cucumber's "or" tag expression
String[] configuredTags = BuildConfig.CUCUMBER_TAGS.split(" ");
Collection<String> scenarioTags = scenario.getSourceTagNames();
for (String configuredTag : configuredTags) {
for (String scenarioTag : scenarioTags) {
if (configuredTag.equals(scenarioTag)) {
return true;
}
}
}
return false;
}
@Before(order=1)
public void assumeTagsMatch(final Scenario scenario) {
Assume.assumeTrue("Only run scenarios that match configured tags", checkTagsMatch(scenario));
}
}
This just makes it skip tests that don't match one of the tags - I can invoke like gradlew connectedCheck -Pcucumbertags="@one @two"
.
It's not ideal at all, as those tests still show up in reports but marked as skipped, and also my solution doesn't support tag expressions. If anyone has any better ideas, I would be interested to hear.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | arekolek |
Solution 2 | Adam Burley |