Gradle task check if property is defined - gradle

I have a Gradle task that executes a TestNG test suite.
I want to be able to pass a flag to the task in order to use a special TestNG XML suite file (or just use the default suite if the flag isn't set).
gradle test
... should run the default standard suite of tests
gradle test -Pspecial
... should run the special suite of tests
I've been trying something like this:
test {
if (special) {
test(testng_special.xml);
}
else {
test(testng_default.xml);
}
}
But I get a undefined property error. What is the correct way to go about this?

if (project.hasProperty('special'))
should do it.
Note that what you're doing to select a testng suite won't work, AFAIK: the test task doesn't have any test() method. Refer to https://discuss.gradle.org/t/how-to-run-acceptance-tests-with-testng-from-gradle/4107 for a working example:
test {
useTestNG {
suites 'src/main/resources/testng.xml'
}
}

This worked for me:
test {
if (properties.containsKey('special')) {
test(testng_special.xml);
}
else {
test(testng_default.xml);
}
}

Here are 3 solutions for Kotlin DSL (build.gradle.kts):
val prop = project.properties["myPropName"] ?: "myDefaultValue"
val prop = project.properties["myPropName"] ?: error("Property not found")
if (project.hasProperty("special")) {
val prop = project.properties["myPropName"]
}
Note that you can omit the project. prefix as it is implicit in Gradle build files.

From Gradle Documentation:
-P, --project-prop
Sets a project property of the root project, for example -Pmyprop=myvalue
So you should use:
gradle test -Pspecial=true
with a value after the property name

Related

How to include some deps and source codes in Gradle conditionally

In a Maven project, it is easy to add extra deps and include extra source codes via defining a new Maven profile.
How to do the following things in a Gradle project.
Includes extra deps
Includes another source codes directory
And for example, use an extra property existence(eg. add to command line) to decide to activate it or not. I am not sure the best way in Gradle world.
I am not recommending your approach.
But it can be done via - project properties from gradle command line and groovy if (condition) { } for dependencies and multiple sourceset defs
on command line
gradle build -PbProfile=extra1
ext.buildFlag = 'default'
if (project.hasProperty('bProfile')) {
ext.buildFlag = property('bProfile')
}
println "running profile - ${buildFlag}"
dependencies {
//common-deps
if ("extra1".equals(buildFlag)) {
//extra deps
}
}
if ("extra1".equals(buildFlag)) {
//custom sourceset def
} // more else if needed
I use conditionally applied sub-configurations. This is done thru the apply from directive:
if (project.hasProperty('browsers')) {
ext.browsers.split(',').each {
def browser = it.trim()
if (browser) {
apply from: "${browser}Deps.gradle"
}
}
}
This block checks for specification of the browsers property (either from gradle.properties or the -P command line argument). If this property is defined, I split the property's value on commas and apply sub-configurations whose names conform to the pattern <browser>Deps.gradle.
The project in which I use this pattern is here

How to include/exclude junit5 tags in gradle cmd?

I want to execute tagged JUnit 5 tests, eg. only slow tests, with gradle.
I want to do the same like this in maven:
mvn test -Dgroups="slow"
But what is the equivalent in gradle? Or is there anything at all?
To execute all JUnit 5 tests which are marked with #Tag("slow"). I know it's quite simple to create a dedicated task like this:
tasks.withType(Test::class.java).configureEach {
useJUnitPlatform() {
includeTags("slow")
}
}
But I have a lot of different tags and I don't want to have a task for each single tag. Or worse, having one task for all permutations.
Another possibility would be to pass self defined properties to the task like this
tasks.withType(Test::class.java).configureEach {
val includeTagsList = System.getProperty("includeTags", "")!!.split(",")
.map { it.trim() }
.filter { it.isNotBlank() }
if (includeTagsList.isNotEmpty()) {
includeTags(*includeTagsList.toTypedArray())
}
}
The last time I checked, Gradle didn't have built-in support for configuring JUnit Platform include and exclude tags via the command line, so you'll have to go with your second approach.
But... there's no need to split, map, and filter the tags: just use a tag expression instead: https://junit.org/junit5/docs/current/user-guide/#running-tests-tag-expressions
You can create a separate task and to choose do you want to run the task or to skip it.
For example add this code in build.gradle:
def slowTests= tasks.register("slowTests", Test) {
useJUnitPlatform {
includeTags "slow"
}
}
Now if you want to run only slow tests:
./gradlew clean build -x test slowTests

Changing from the default "test" directory for Spring Contract's contracts and base class tests

I have tried changing the default test directory name for base test classes as follows:
Old:
bignibou-server/src/test/java/com/bignibou/signup
New:
bignibou-server/src/contracts/java/com/bignibou/signup
Here is the directory where my contracts live:
New:
bignibou-server/src/contracts/resources/contracts/signup
Here is my gradle configuration:
contracts {
packageWithBaseClasses = 'com.bignibou'
baseClassMappings {
baseClassMapping(".*signup*.", "com.bignibou.signup.SignupBase")
}
}
I use the same gradle configuration both for contracts tests & integration tests. See:
sourceSets {
integrationTest {
java.srcDirs = ['src/it/java', 'src/contracts/java']
resources.srcDirs = ['src/it/resources', 'src/contracts/resources']
compileClasspath = sourceSets.main.output + configurations.testRuntime
runtimeClasspath = output + compileClasspath
}
}
However, since I moved my base class tests & contracts from the test directory, the contracts tests are nor run...
edit:
After searching the documentation, I found the contractsDslDir property that can be used as follows:
contracts {
packageWithBaseClasses = 'com.bignibou'
contractsDslDir = new File("${project.rootDir}/src/contracts/resources/contracts")
baseClassMappings {
baseClassMapping(".*signup*.", "com.bignibou.signup.SignupBase")
}
}
However, the tests are still not run... What else I am missing?
Here is how I try to run the tests:
./gradlew clean check
edit 2:
I was able to get Spring Cloud Contract to find my contract using the following value for contractsDslDir:
contractsDslDir = new File("./src/contracts/resources/contracts")
Now the issue is that my test won't find the test base:
> Task :bignibou-server:compileTestJava FAILED
/Users/julien/Documents/projects/bignibou/bignibou-server/build/generated-test-sources/contracts/com/bignibou/SignupTest.java:3: error: package com.bignibou.signup does not exist
import com.bignibou.signup.SignupBase;
^
/Users/julien/Documents/projects/bignibou/bignibou-server/build/generated-test-sources/contracts/com/bignibou/SignupTest.java:20: error: cannot find symbol
public class SignupTest extends SignupBase {
^
symbol: class SignupBase
2 errors
FAILURE: Build failed with an exception.
edit 3: It is odd: Spring Cloud Contracts still seems bound to the test gradle task (as opposed to my custom integrationTest one)...
When I run ./gradlew clean integrationTest the contracts are not even searched... However when I run ./gradlew clean test I get the above error indicating that Spring Cloud Contract is looking for a base class but not finding one.
How can I tell Spring Cloud Contract to bind to my custom integrationTest gradle task?
It's not supported at the moment. Feel free to find the issue or create a new one if you can't find it

Conditional logic in gradle build to only execute certain code when a specific task is running

So, I am using a plugin in my gradle build (the plugin is org.flywaydb.flyway but that is not really relevant). I want to validate the caller has passed in a runtime parameter when tasks from this plugin are executing but not when other tasks are executing.
I pass options to the flyway plugin based on a supplied parameter. I want an error to be returned when a flywayTask is being executed and no parameter is supplied. When a non-flyway task is being run, I do not want to validate if the parameter is supplied.
gradle -PmyParam=myValue flywayMigration
=> should run code and there should be no error
gradle flywayMigration
=> should run code and should produce error (as no parameter supplied)
gradle jar
=> should not run code and no error should be produced
I have been reading about gradle configuration and execution which is fine but I still can't find a way to only run the code when the flyway plugin is bveing executed OR specific flyway tasks are being executed.
This is my current code:
if(gradle.taskGraph.hasTask("flywayMigrate")) {
flyway {
def dbCode, dbUser, dbPassword, dbUrl
if (!project.hasProperty("db_env")) {
throw new GradleException("Expected db_env property to be supplied for migration task. Can be passed" +
" at command line e.g. [gradle -Pdb_env=ex1 flywayMigrate]")
} else {
// do stuff
}
user = balh
password = blah
url = blah
driver = 'oracle.jdbc.OracleDriver'
cleanDisabled = true
baselineOnMigrate = true
baselineVersion = '1.0.0'
}
}
To be clear, I only want this code:
if (!project.hasProperty("db_env")
to run for flyway tasks.
The code above throws this error:
Task information is not available, as this task execution graph has not been populated.
I've tried a few things here, any advice would be appreciated.
It's not really clear to me, what exactly do you want to do in case if this property is provided, but I think, you can do it without accesing task graph, just try to use doFirst Closure of the flywayMigrate task. Just something like this:
flywayMigrate.doFirst {
if(!project.hasProperty("db_env")) {
throw ...
} else {
//Do something
}
}
And leave your plugin configuration free of any additional logic.
As for exception, have you tried to wait until graph is ready? It's usualy done as follows:
gradle.taskGraph.whenReady {taskGraph ->
if(gradle.taskGraph.hasTask("flywayMigrate")) {
...
}
}
Update: to answer the question from the comments
if I can attach doFirst to multiple tasks?
Yes, you can use somthing like:
//declare task names
def names = ["taskA", "taskB", "taskC"]
tasks.findAll {it ->
//filter tasks with names
if (it.name in names)
return it
}.each { it ->
//add some extra logic to it's doFirst closure
it.doFirst {
println 'hello'
}
}
Just check, that all the tasks are exists before this configuration.

Gradle Plugin Test UpToDateWhen method

I'm writing a gradle plugin that defines an upToDateWhen closure to skip the task when certain criteria is met. I'm having trouble figuring out how to wrap a test around this method. Currently it looks like:
class MyCoolTask extends DefaultTask {
MyCoolTask() {
outputs.upToDateWhen {
if (somecondition)
return true
else
return false
}
}
}
My test looks like this:
class MyCoolTaskTest {
#Test
void testUpToDateCheck() {
project = ProjectBuilder.builder().build()
project.apply plugin: 'myCoolPlugin'
project.myCoolTask.execute()
// But then how do you do a subsequent run and ensure that the task did not execute?
project.myCoolTask.execute() // running this a second time does not work.
project.myCoolTask.outputs.upToDateWhen() // Throws a syntax error
}
}
Any insight that could be offered would be great! Thanks!
ProjectBuilder is meant for low-level tests that configure the build but don't execute any tasks. You can either factor out the contents of outputs.upToDateWhen { ... } into a method/class and test that, and/or write an acceptance test that executes a real build using the Gradle tooling API.

Resources