I'm working in a spring boot project to automate integration tests with gradle. I started working recently in a new enterprise, and my colleagues run integration tests as follows:
In the build.gradle file there is an integrationTest task
sourceSets {
integrationTest {
java {
compileClasspath = test.output + main.output + compileClasspath
runtimeClasspath = test.output + main.output + runtimeClasspath
}
resources.srcDir file('src/test/resources')
}
}
configurations {
mapstruct
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
compileJava {
options.compilerArgs = [
'-Amapstruct.defaultComponentModel=spring'
]
}
test {
ignoreFailures = true
}
task integrationTest(type: JavaExec) {
classpath = sourceSets.integrationTest.runtimeClasspath
}
After launching the task the application starts running in a designated port, then they open postman, import a collection and run the tests.
My job is to find a way to skip the extra clicks, i.e. run the postman collections automatically.
The first idea was to use the postman-runner gradle plugin but I cannot add it to the project due to connectivity issues of enterprise computers.
The second idea which I am currently working on, is to run newman on a powershell script and save the output.
The problem is that in gradle you can execute a task once another task is finished, but the integrationTest task never finishes. It launches the application in a port and keeps listening for requests. Is there a way to run the other task, which executes the powershell script, after the application has started running on the port and while it is waiting for requests ?
Thank you!
Simple answer: By design, it is not possible in Gradle to execute a task while another task is still running.
However, there might still be a solution for your problem. A task in Gradle is just a concept of something that needs to be done as a part of your build. But it does not necessarily represent a single process. Gradle might run a process using two tasks, one to start the process and another one to stop the process. The following Gradle "pseudo-code" shows an example of this idea.
def process
task start {
doFirst {
process = startProcess()
}
finalizedBy 'stop'
}
task stop {
dependsOn 'start'
doFirst {
process.stop()
}
}
This example even uses Gradle functionality to ensure that each time start is executed, stop will be executed later on and vice-versa. The Gradle Docker plugin uses a similar concept with its task types DockerStartContainer and DockerStopContainer.
Regarding your use case, you could use one task to start the application listening to a port, another task that runs the actual tests (using Postman) and another task that stops the application once the tests are finished:
task startApp {
doFirst {
println 'Starting app'
}
finalizedBy 'stopApp'
}
task integrationTest {
doFirst {
println 'Running integration tests'
}
dependsOn 'startApp'
finalizedBy 'stopApp'
}
task stopApp {
doFirst {
println 'Stopping app'
}
dependsOn 'startApp'
}
This second example is a valid build.gradle file that may be used as a template. When running gradle integrationTest, you will see that the tasks are executed in the correct order. Now all that is missing is the actual functionality of the tasks. Sadly, the JavaExec task type provided by Gradle does not support this kind of async execution, so you might need a third-party plugin or create your own task type (maybe based on ProcessBuilder). You might even wrap your application in a Docker container and use the plugin mentioned above.
Related
I created Spring Boot project which uses gradle build system. I want to run one separate test class by custom gradle task to be able depend on it in other tasks. Now I can do it with this code:
import org.apache.tools.ant.taskdefs.condition.Os
def gradleWrapper = Os.isFamily(Os.FAMILY_WINDOWS) ? 'gradlew.bat' : './gradlew'
task runMyTest(type: Exec) {
workingDir "$rootDir"
commandLine gradleWrapper, ':test', '--tests', 'com.example.MyTest'
}
Obviously, this is not a very beautiful solution, because it launches an additional Gradle daemon. I tried before another solution:
task runMyTest(type: Test, dependsOn: testClasses) {
include 'com.example.MyTest'
}
But it is not working (do not execute my test class).
UPD: I tried yet another solution:
task runMyTest(type: Test) {
filter {
includeTestsMatching "com.example.MyTest"
}
}
It fails with this error message:
Execution failed for task ':runMyTest'.
> No tests found for given includes: [com.example.MyTest](filter.includeTestsMatching)
However, obviously, my test exists, since running the test through the command line produces the correct result.
UPD2: I missed useJUnitPlatform() inside my test task. It was in the default test task (written to my build.gradle by Spring Boot initializer), but not in the custom task.
You can do it using a TestFilter.
Using includeTestsMatching you can specify your class.
If you need to specify a single test method, you can use includeTest "com.example.MyTest", "someTestMethod".
task runMyTest(type: Test) {
useJUnitPlatform()
filter {
includeTestsMatching "com.example.MyTest"
}
}
I have a somewhat complex deployment task as well as an integration testing task. At the moment, my integration testing task depends on the deployment task. However, I'd like to have a sort of wrapper task for the integration testing task such that I can build a task to just run the integration test against whatever is currently running and a task to deploy, run the integration test, and then take down the deployment again.
This is the current state:
task integrationTest(type: Test, dependsOn: "startWebappNodes") {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
outputs.upToDateWhen { false }
}
task runIntegrationTests(type: Exec, dependsOn: "integrationTest") {
commandLine 'docker-compose', 'down'
doLast {
println "Integration tests running finished"
}
}
How can I write a task, let's call it executeIntegrationTest, that executes the integration test without depending on startWebAppNodes and then also have a test that runs startWebAppNodes, then executeIntegrationTest, then brings down the nodes again?
There is a simplier way: use two main tasks for running integrationTest:
the first one (your current integrationTest task) to execute tests in a standalone way (without deploy/shutdown),
the second one (your current runIntegrationTests task) to wrap the first task, and handle the deploy/shutdown nodes.
1) Remove the dependsOn dependency between integrationTest task and startWebappNodes
=> you can execute this integrationTest task in a "standalone" way ( against whatever is currently running )
./gradlew integrationTest
// execution of dependent task
> Task :integrationTest
// .. test executing...
2) Update your runIntegrationTests task to make it depends on both integrationTest and startWebappNodes tasks, and also add a constraint to make startWebappNodes execute before integrationTest, using mustRunAfter
task runIntegrationTests(type: Exec) {
group "test"
dependsOn startWebappNodes
dependsOn integrationTest
commandLine 'docker-compose', 'down'
doLast {
println "Integration tests running finished"
}
}
integrationTest.mustRunAfter startWebappNodes
=> when executing task runIntegrationTests it will start nodes, execute integration test, then shutdown nodes
./gradlew runIntegrationTests
// execution of dependent task
> Task :startWebappNodes
> Task :integrationTest
// .. test executing...
> Task :runIntegrationTests
Integration tests running finished
Currently I have task which starts Google Cloud server, runs tests and stops server. It is defined at root project:
buildscript {...}
allprojects {...}
task startServer (dependsOn: "backend:appengineStart") {}
task testPaid (dependsOn: "app:connectedPaidDebugAndroidTest") {}
task stopServer (dependsOn: "backend:appengineStop") {}
task GCEtesting {
dependsOn = ["startServer",
"testPaid",
"stopServer"]
group = 'custom'
description 'Starts GCE server, runs tests and stops server.'
testPaid.mustRunAfter 'startServer'
stopServer.mustRunAfter 'testPaid'
}
I tried multiple ways to write it something like this, short with only one task. I didn't get how to refer to task from another project and call mustRunAfter on it. This doesn't work (I also tried to refer from Project.tasks.getByPath, root.tasks, etc):
task GCEtesting {
dependsOn = ["backend:appengineStart",
"app:connectedPaidDebugAndroidTest",
"backend:appengineStop"]
group = 'custom'
description 'Starts GCE server, runs tests and stops server.'
"app:connectedPaidDebugAndroidTest".mustRunAfter "backend:appengineStart"
"backend:appengineStop".mustRunAfter "app:connectedPaidDebugAndroidTest"
}
Is it possible? What is correct syntax to make this work?
It looks like your problem is that you're treating dependsOn as meaning "invoke this task". It actually means "ensure the result of the depended on task is available before the dependent task starts". That's why your first solution didn't work: statements like testPaid.mustRunAfter only affect the actions of the testPaid task itself, not its dependencies.
Anyway, you can get the behaviour you want using dependsOn and finalizedBy, but they have to be declared in the build file of the app subproject.
app/build.gradle:
task connectedPaidDebugAndroidTest {
//
//...
//
dependsOn 'backend:appengineStart' // Ensure appengineStart is run at some point before this task
finalizedBy 'backend:appendginStop' // Ensure appengineStop is run at some point after this task
}
Then, you can run your tests simply with gradle app:connectedPaidDebugAndroidTest. If you really want to define a task in the root project to run the tests, then that's easy too:
build.gradle:
task GCEtesting {
dependsOn = "app:connectedPaidDebugAndroidTest"
}
I have code:
task instalNodeModules {
doLast {
npmInstall.execute()
installGulp.execute()
}
}
task runTasks {
doLast {
instalNodeModules.execute()
gulpBuildWithOpts.execute()
}
}
Gradle 4.5.1 warns about it as
The TaskInternal.execute() method has been deprecated and is scheduled
to be removed in Gradle 5.0. There are better ways to re-use task
logic, see
https://docs.gradle.org/4.5.1/userguide/custom_tasks.html#sec:reusing_task_logic.
But link does not give me any hint how I could replace it because I can’t just setup task dependencies like dependsOn or finalizdBy - it called not from other task but from end of build.
You are not supposed to call the execute method directly, but rather model your tasks dependencies. This allows gradle to perform up-to-date checks and only execute the tasks that are needed.
task runTasks {
dependsOn installNodeModules
dependsOn gulpBuildWithOpts
}
I have gradle set up and running. My build.gradle has 2 tasks defined inside:
task setVersion() {
println('setVersion')
//...
}
task setIntegrationEnv() {
println('setIntegrationEnv')
//...
}
When I run
./gradlew clean
gradle runs both tasks setVersion and setIntegrationEnv and then it runs clean for all my modules (app, cloud_module) in that project, output:
Relying on packaging to define the extension of the main artifact has been deprecated and is scheduled to be removed in Gradle 2.0
setVersion
setIntegrationEnv
:cloud_module:clean
:app:clean
BUILD SUCCESSFUL
Total time: 14.18 secs
Why this happens, where this behavior is defined?
Could You please provide full build.gradle script? I'd be much easier to help You. You've probably mistaken gradle build phase with configuration phase - it's a common topic here.
General rule is that code You'd like to be run at build phase should be added as an action:
task someTask << {
println 'runtime'
}
while code You'd like to run at configuration phase should be added in task body:
task someTask {
println 'configuration
}
or all together:
task someTask {
println 'configuration'
doLast {
println 'runtime'
}
}
Additional info can be found here, here and here.