I am using Gradle with TestNG. I have this build.gradle:
useTestNG() {
useDefaultListeners = true
suites "src/test/resources/tests1.xml"
suites "src/test/resources/tests2.xml"
}
}
How can I run for example only tests1.xml from command line?
you can use project properties to add/change/... different suites. In you example you are probably running
gradlew test
which run both suites. If you modify test task in your build.gradle
def suite1 = project.hasProperty("suite1")
def suite2 = project.hasProperty("suite2")
test {
useTestNG() {
dependsOn cleanTest
useDefaultListeners = true
if(suite1) {
suites "src/test/resources/simpleSuite.xml"
}
if(suite2) {
suites "src/test/resources/advancedSuite.xml"
}
}
}
you can choose suite in this way
gradlew test -Psuite1
gradlew test -Psuite2
gradlew test -Psuite1 -Psuite2
you can specify variable let say suiteFile with default value and use it in testNG section. For example:
ext{
set suiteFile, default is 'testrun_config.xml'
if (!project.hasProperty('suiteFile')) {
suiteFile = 'testrun_config.xml'
}
}
test {
useTestNG() {
dependsOn cleanTest
useDefaultListeners = true
suites "src/test/resources/"+suiteFile
}
}
Refer qaf gradle build file
If you want to pass through command line
gradlew test -PsuiteFile=test.xml
In build.gradle update:
test {
useTestNG() {
if (project.hasProperty('suite1')) { suites './src/test/suite1.xml' }
if (project.hasProperty('suite2')) { suites './src/test/suite2.xml' }
}
}
Use command gradlew test -Psuite1 to run suite1 and similarly update for suite2 as well.
Related
I've created a new test suite called integrationTest using the jvm-test-suite plugin.
Now I want to generate a jacoco report just for the integration tests.
I create a new gradle task like this:
tasks.create<JacocoReport>("jacocoIntegrationTestReport") {
group = "verification"
description = "Generates code coverage report for the integrationTest task."
executionData.setFrom(fileTree(buildDir).include("/jacoco/integrationTest.exec"))
reports {
xml.required.set(true)
html.required.set(true)
}
}
But the generated HTML/XML report is empty. I have run the integration tests before executing the task and the file integrationTest.exec exists.
Thanks
It seems the important part of the new JaCoCo report task configuration is to wire the execution data via the integrationTest task instead of the exec-file path. The official docs (see last example here) also imply that the source set must be wired as well.
Here is the full build script (Gradle 7.6) that produces a report with command:
./gradlew :app:integrationTest :app:jacocoIntegrationTestReport
// build.gradle.kts
plugins {
application
jacoco
}
repositories {
mavenCentral()
}
testing {
suites {
val integrationTest by creating(JvmTestSuite::class) {
dependencies {
implementation(project(":app"))
}
}
}
}
tasks.register<JacocoReport>("jacocoIntegrationTestReport") {
executionData(tasks.named("integrationTest").get())
sourceSets(sourceSets["integrationTest"])
reports {
xml.required.set(true)
html.required.set(true)
}
}
I have a Spring Project where I wrote some Unit and Integration Tests. Now I want to create custom tasks for running all unit tests, all integration tests and one task for running both. But how can I do this?
I would suggest separating integration tests into separate source set. By default you already have 2 source sets, one for production code and one for tests. To create a new source set (create new directory under src/integrationTest/java) and add a following configuration using Junit5:
test {
useJUnitPlatform()
}
sourceSets {
integrationTest {
java.srcDir file("src/integrationTest/java")
resources.srcDir file("src/integrationTest/resources")
compileClasspath += sourceSets.main.output + configurations.testRuntime
runtimeClasspath += output + compileClasspath
}
}
For separate task:
task integrationTest(type: Test) {
description = 'Runs the integration tests.'
group = 'verification'
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
useJUnitPlatform()
reports {
html.enabled true
junitXml.enabled = true
}
}
Now you have 3 tasks available:
gradlew test
gradlew integrationTest
gradlew check - runs both as it depends on Test task which both extend
If you also are using jacoco and want to merge the test results then you can have following tasks:
task coverageMerge(type: JacocoMerge) {
destinationFile file("${rootProject.buildDir}/jacoco/test.exec")
executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")
}
// aggregate all coverage data at root level
if (tasks.findByName("test")) {
tasks.findByName("test").finalizedBy coverageMerge
}
if (tasks.findByName("integrationTest")) {
tasks.findByName("integrationTest").finalizedBy coverageMerge
}
task codeCoverageReport(type: JacocoReport) {
executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")
subprojects.each {
if (!it.name.contains('generated')) {
sourceSets it.sourceSets.main
}
}
reports {
xml.enabled true
html.enabled true
html.setDestination(new File("${buildDir}/reports/jacoco"))
csv.enabled false
}
}
To run coverage report just execute
gradlew codeCoverageReport
I am using TestNG and Gradle, what I am trying to achieve is, if the task that runs the tests fails, run another task which is also of type test which actually sends the test report. If all tests pass, don't do anything, I know about finalizedBy, but that sends the test report either way.
I tried something like this, but no luck.
task uiTest(type: Test) {
useTestNG() {
environment "DOMAIN", "${DOMAIN}"
useDefaultListeners = true
suites "src/test/resources/ui-tests/ThemeA/chrome67.xml"
}
reports {
html {
enabled true
}
reports.html.destination = "build/reports/TestReports/uiTestThemeA"
}
testLogging {
showStandardStreams = true
exceptionFormat "full"
events = ["started", "passed", "skipped", "failed"] //, "standard_out", "standard_error"]
showExceptions = true
showStackTraces = true
}
}
task testing(dependsOn: uiTest, type: Test) {
boolean state = uiTestThemeA.state.executed
if(!state) {
println name
useTestNG() {
suites "src/test/resources/ui-tests/sendReport.xml"
}
}
}
If I understand you correctly, then the following should do what you need. Replace your testing task with the following one and configure your uiTest task to be finalized by the testing task:
task testing(type: Test) {
onlyIf { uiTest.state.failure }
useTestNG() {
suites "src/test/resources/ui-tests/sendReport.xml"
}
}
uiTest {
finalizedBy testing
}
Some notes:
the onlyIf statement does the magic you are looking for
this assumes you execute uiTest
build.gradle
test {
jvmArgs "-DisParallel=true"
useTestNG(){
suites testngxml
}
}
when i am trying to run gradlew clean test -Dgroups='sanityTests' will not run tests in that specific group but will run all tests. Can you please provide some insight on this.
test {
jvmArgs "-DisParallel=true"
useTestNG(){
suites testngxml
includeGroups System.properties['groups']
}
}
We can pass by adding includeGroups inside test which is shown below:
test {
useTestNG() {
dependsOn cleanTest
useDefaultListeners = true
includeGroups groupName
}
}
I use the following annotation to tag my integration tests:
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#Tag("integration-test")
public #interface IntegrationTest {
}
This is the filter I use in build.gradle to exclude these tests from gradle build:
junitPlatform {
filters {
tags {
exclude 'integration-test'
}
}
}
So far, so good.
Now I would like to offer a Gradle task which specifically runs my integration tests – what's the recommended approach?
Based on https://github.com/gradle/gradle/issues/6172#issuecomment-409883128
Amended in 2020 to take lazy task configuration and Gradle 5 into account. See answer's history for older versions.
plugins {
id "java"
}
def test = tasks.named("test") {
useJUnitPlatform {
excludeTags "integration"
}
}
def integrationTest = tasks.register("integrationTest2", Test) {
useJUnitPlatform {
includeTags "integration"
}
shouldRunAfter test
}
tasks.named("check") {
dependsOn integrationTest
}
Running
gradlew test will run tests without integration
gradlew integrationTest will run only integration test
gradlew check will run test followed by integrationTest
gradlew integrationTest test will run test followed by integrationTest
note: order is swapped because of shouldRunAfter
History
Gradle 4.6+ supports JUnit 5 natively
JUnit 5 deprecated their plugin: https://github.com/junit-team/junit5/issues/1317
JUnit 5 deleted plugin: 'org.junit.platform.gradle.plugin'
JUnit 5 closed junit5#579 (same as OP's question) as won't-fix (due to decommissioning their plugin)
Gradle supports the above feature: https://github.com/gradle/gradle/issues/6172
Tip
Note: while the above works, IntelliJ IDEA has a hard time inferring stuff, so I suggest to use this more explicit version where everything is typed and code completion is fully supported:
... { Test task ->
task.useJUnitPlatform { org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions options ->
options.includeTags 'integration'
}
}
build.gradle.kts
Root project Kotlin DSL drop-in for configuring integration tests in all modules in Gradle 5.6.4
allprojects {
plugins.withId("java") {
#Suppress("UnstableApiUsage")
this#allprojects.tasks {
val test = "test"(Test::class) {
useJUnitPlatform {
excludeTags("integration")
}
}
val integrationTest = register<Test>("integrationTest") {
useJUnitPlatform {
includeTags("integration")
}
shouldRunAfter(test)
}
"check" {
dependsOn(integrationTest)
}
}
}
}
I filed an issue: https://github.com/junit-team/junit5/issues/579 (as suggested by Sam Brannen).
Meanwhile, I am using a project property as a workaround:
junitPlatform {
filters {
tags {
exclude project.hasProperty('runIntegrationTests') ? '' : 'integration-test'
}
}
}
Consequently, integrations tests will be skipped with:
gradle test
but will be included with:
gradle test -PrunIntegrationTests
Gradle 6
I am not sure if it is because Gradle behavior has changed, but the highest voted answer did not work for me in Gradle. 6.8.3. I was seeing the integrationTests task run along with the main test task. This simplified version worked for me:
test {
useJUnitPlatform {
excludeTags "integration"
}
}
tasks.register("integrationTests", Test) {
useJUnitPlatform {
includeTags "integration"
}
mustRunAfter check
}
Commands:
./gradlew test or ./gradlew clean build - Runs tests without
'integration' tag.
./gradlew integrationTests - Only runs test with
'integration' tag.
According to me, the best, current working code to solve this, is the one presented by: TWiStErRob found here.
Note that tests must be tagged with ui in the example below (Junit5 tagging):
task uiTest(type: Test) {
useJUnitPlatform {
includeTags 'ui'
excludeTags 'integration'
}
}
Howe ever, I did not managed to get the Junit5 test-suit-thing to be run from gradle directly witch I would think would be an even nicer solution. But I think the solution buy TWiStErRob, is good enough. The down side is that the gradle.build file now also will be bloated with test-suit-things.
Please note that it is fine to create multiple test suites in the gradle file like this:
task firstTestSuite(type: Test) {
useJUnitPlatform {
includeTags 'test-for-first-test-suite'
}
}
task secondTestSuite(type: Test) {
useJUnitPlatform {
includeTags 'test-for-second-test-suite'
}
}
Then then all could be run separately like this:
gradlew firstTestSuite
gradlew secondTestSuite
gradlew ui
Solution run with Gradle 6.6.1
A similar approach to Rahel Lüthy avoiding the usage of empty strings, in this case to run all tests or just some tags:
test {
useJUnitPlatform() {
if (project.hasProperty("includes")) {
includeTags(project.property("includes") as String)
}
}
}