Launch TestNG debug with JVM args from build.gradle - java

I have a Gradle project where the tests run using TestNG. (I use Intellij IDEA)
I need an aspectjweaver to be launched each time on tests execution. The aspectjweaver is defined as JVM argument and added to applicationDefaultJvmArgs in Build.gradle:
version '1.0'
apply plugin: 'application'
def aspectjVersion = "1.8.7"
mainClassName = 'exa.tags.runner.TestManager'
applicationDefaultJvmArgs=["-javaagent:APP_HOME_PLACEHOLDER/lib/aspectjweaver-${aspectjVersion}.jar"]
dependencies {
compile "org.aspectj:aspectjweaver:${aspectjVersion}"
}
def weaver = configurations.compile.find { it.name.contains("aspectjweaver") }
startScripts << {
unixScript.text = unixScript.text.replace('APP_HOME_PLACEHOLDER', '\$APP_HOME')
windowsScript.text = windowsScript.text.replace('APP_HOME_PLACEHOLDER', '%APP_HOME%')
}
run {
doFirst {
jvmArgs = jvmArgs.grep {
!it.contains('APP_HOME_PLACEHOLDER')
}
jvmArgs = jvmArgs << "-javaagent:$weaver"
}
}
So when I launch tests using this run task, everything works fine for me.
But the problem is that when I right click on a test and select TestNG Run or Debug, the aspectj is not launched.
When I used maven, the aspectjweaver was added as an argLine to the maven-surefire-plugin and worked each time (debug, run single test or execute mvn test goal).
So, what is the proper way and place to set the JVM argument to the actual version of AspectJ? I tried to add the path to aspecj.jar as default arg in Edit configurations. That worked, but in such a case I depend on a specific cached file.
Can this be done somewhere in project files without changing the IDE configurations?
UPDATE
I tried to add a test task in my build.gradle:
def weaver = configurations.compile.find { it.name.contains("aspectjweaver") }
test {
useTestNG()
testLogging.showStandardStreams = true
println "Adding aspectJ weaver: ${weaver}"
jvmArgs = jvmArgs << "-javaagent:$weaver"
}
And now I see this output on each task execution (e.g. when I run clean task).
But when I right click on test and select Debug
I do not see the output.
Looks like the task 'test' is not executed when I select run one test with TestNG.

Related

Run Checkstyle before gradle build on build.gradle.kts and make build fail if there are issues

I added Checkstyle to my Gradle project's build.gradle.kts succesfully:
plugins {
...
checkstyle
}
checkstyle {
toolVersion = "10.3.2"
isIgnoreFailures = false // Added this so that the tasks fail if CheckStyle errors are present.
}
I now have access to CheckStyle's tools with:
gradle checkstyleMain
gradle checkstyleTest
They both work as expected showing warnings and generating HTML reports.
I now want to make the gradle run task run the checkstyleMain task before itself and to fail if there are CheckStyle issues.
Is there a way to do this? The only references I've found so far are not very clear and are for Groovy and Android instead of Kotlin and Java build.gradle files.
To make task depends on another task you need to use dependsOn:
bootRun.dependsOn(checkstyleMain)
For failing in case of error/warning it is possible to use following configuration for checkstyle:
ignoreFailures = false
maxWarnings = 0
maxErrors = 0
This is what worked for me:
checkstyle {
toolVersion = "10.3.2"
isIgnoreFailures = false
maxWarnings = 0
maxErrors = 0
}
val build = "build"
tasks[build].dependsOn("checkstyleMain")
Now gradle build fails if there are errors found in checkstyleMain .

Spring Boot Gradle quickbuild that skips the non-essentials

Java 8 and Gradle 4.6 here. I have a Spring Boot app with the following build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.8.RELEASE")
}
}
plugins {
id 'java-library'
id 'checkstyle'
id 'jacoco'
}
apply plugin: 'org.springframework.boot'
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
configurations {
dev
}
dependencies {
compile(
,'ch.qos.logback:logback-classic:1.2.3'
,'org.slf4j:jul-to-slf4j:1.7.25'
,'org.apache.logging.log4j:log4j-to-slf4j:2.9.1'
,'org.apache.commons:commons-lang3:3.7'
,'org.springframework.boot:spring-boot-starter-actuator'
,'org.apache.commons:commons-text:1.2'
,'commons-beanutils:commons-beanutils-core:1.8.3'
)
testCompile(
'junit:junit:4.12'
,'org.mockito:mockito-core:2.23.0'
)
dev('org.springframework.boot:spring-boot-devtools')
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
String buildName = 'myapp'
jar {
baseName = buildName
}
bootRun {
if(project.hasProperty('debugMode')) {
jvmArgs = [ "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" ]
}
classpath = sourceSets.main.runtimeClasspath + configurations.dev
}
checkstyle {
toolVersion = '8.12'
ignoreFailures = false
}
jacocoTestReport {
reports {
xml.enabled false
html.enabled true
html.destination file("${buildDir}/reports/jacoco/")
}
}
check.dependsOn jacocoTestCoverageVerification
jacocoTestCoverageVerification.dependsOn jacocoTestReport
So this is a Spring Boot Java app that also uses the Checkstyle and Jacoco Gradle plugins.
I consider a "full build" to be an invocation that:
Compiles
Runs Checkstyle
Runs unit tests (JUnit)
Runs Jacoco for code coverage analysis
Uses Spring Boot's libraries to build a "fat" (self-contained) executable jar
Given my current Gradle build file, I run a full build like so:
./gradlew clean build
However this can take several minutes to run through all the unit tests and has become cumbersome. I would like to introduce a
"quick build" option that only compiles the code and creates the Spring Boot fat jar for me. This will help speed up development
time tremendously.
I'm hoping to invoke the quick build like so:
./gradlew clean quickbuild
So far I've got this:
tasks.register("quickbuild") {
doLast {
// ???
}
}
But not sure how to link the compilation and fatjar tasks to it (and more importantly; skipping all the other stuff that I don't want!). Any ideas as to what I'm missing?
Update
The bootJar task doesn't seem to exist or be configured (please check my build.gradle file provided above!):
$ ./gradlew clean bootJar
FAILURE: Build failed with an exception.
* What went wrong:
Task 'bootJar' not found in root project 'myapp'. Some candidates are: 'bootRun'.
* Try:
Run gradlew tasks to get a list of available tasks. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 0s
When I try to run bootRun:
$ ./gradlew clean bootRun
It tries to actually run my app! That's not what I want! I just want to compile and build the fat jar!
See documentation from the Java plugin here : https://docs.gradle.org/current/userguide/java_plugin.html#lifecycle_tasks
You could create a new task (quickbuild) and make it depend on the desired task (in your case it could be the assemble lifecycle task, I guess, or maybe bootJar task (for SpringBoot v2.x) or bootRepackage (for SpringBoot v1.5.x) )
tasks.register('quickbuild'){
dependsOn assemble
}
But if the only purpose of quickbuild task is to trigger the creation of the Jar, the simpliest solution is to execute assemble directly
./gradlew clean assemble

Gradle subproject installDist dependency

I have a project with two subprojects, and am having problems getting the installDist to recognise when it is up-to-date. I have a main build.gradle for the project projName, which has a subproject subproj1 which compiles fine, and another subproject exec which has the following tasks:
task makeMyExec(type: Copy) {
dependsOn(':projName.exec:installDist')
copy {
from rootProject.file("projName/build/install/exec/")
into rootProject.file("../myExec/")
}
}
def createScript(project, mainClass, name) {
project.tasks.create(name: name, type: CreateStartScripts) {
outputDir = new File(project.buildDir, 'scripts')
mainClassName = mainClass
applicationName = name
classpath =
project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files +
project.configurations.runtime
}
project.tasks[name].dependsOn(project.jar)
project.applicationDistribution.with {
into("bin") {
from(project.tasks[name])
fileMode = 0755
}
}
}
Then in the subproject exec I have the following lines of its build.gradle
apply plugin: 'application'
dependencies {
compile project(':subproj1')
}
startScripts.enabled = false
run.enabled = false
createScript(project, 'projName.exec.exec1Main', 'script1')
createScript(project, 'projName.exec.exec2Main', 'script2')
createScript(project, 'projName.exec.exec3Main', 'script3')
The idea is that each of the scripts script1, script2 and script3 should be added to the installDist of subproject exec, so that they need to be created before the copy command runs. Unfortunately the copy command always run first.
This means if I run this twice everything copies properly and I get the scripts in the right place, but unfortunately not if I run it just once.
I would very much appreciate help working out what I have wrong with the dependency here, either the dependency on subproj1 in exec, or the dependency of installDist in the makeMyExec task.
For info - if I only have one mainClass in the subproject and define only
mainClassName ='projName.exec.exec1Main'
applicationName = 'script1'
then this works as intended and all the libs and scripts are built before the copy is done, as the dependency on installDist seems to work then.
I have now found the solution to this problem. If I modify the makeMyExec task to:
task makeMyExec(type: Copy) {
dependsOn('installDist','script1','script2','script3')
from rootProject.file("projName/build/install/exec/")
into rootProject.file("../myExec/")
}
this remove the necessity for the line:
project.tasks[name].dependsOn(project.jar)
and adds the dependency to the copy task of the installDist of the main project and each of the subtasks script1, script2 and script3.
My mistake was to misunderstand that the project.applicationDistribution.with did not actually update the status of :projName.exec:installDist

Jenkins plugin jacoco for code coverage doesn't consider classes exculded from build.gradle in report

Here is part of my build.gradle which I have configured to exclude the classes from code coverage.
test {
jacoco {
append = false
destinationFile = file("$buildDir/reports/jacoco/jacoco.exec")
}
finalizedBy jacocoTestReport
}
jacocoTestReport {
reports {
xml {
enabled true
}
html {
enabled true
}
}
afterEvaluate {
classDirectories = files(classDirectories.files.collect {
fileTree(dir: it,
exclude: ['**/*DAO*.*'])
})
}
}
Here I want to exclude DAO classes in my project, as I am mocking those classes anyways, and I am planning to cover them in DB test cases differently from Unit testing.
When I run below command:
gradle jacocoTestReport
It generates the code coverage report which excludes those classes from code coverage,
${project_dir}\build\reports\jacoco\test\html\index.html
As a reason I have excluded those from gradle build via:
afterEvaluate {
classDirectories = files(classDirectories.files.collect {
fileTree(dir: it,
exclude: ['**/*DAO*.*'])
})
}
But when Jenkins builds the project the code coverage reports generated by jenkins doesn't consider this settings, and the report which is generated by jenkins includes those classes, in coverage report.
Another work around I have tried is to modify the code configuration from 'Post-build Actions' for jacoco plugin in jenkins
I have set my pattern under Exclusions (e.g.: **/*DAO*.*) text field, and this is working fine, test cases are getting excluded from code coverage.
But I wanted to know that is there any way I can avoid configuring jenkins for specifing exclusion patterns and uses the reports genrated by gradle jacocoTestReport on jenkins, because this redudant because I am specifing it in build.gradle and If I have to create multiple branches on jenkins then It is kind of maintainance as well.
According to below post this issue is not yet resolved
https://issues.jenkins-ci.org/browse/JENKINS-15570
Is any one has idea on this?
Note: My Jacoco version is :
jacoco {
toolVersion = "0.7.6.201602180812"
}
and Jacoco plugins version on jenkins is :
2.0.1

How do I add a new sourceset to Gradle?

I want to add integration tests to my Gradle build (Version 1.0). They should run separately from my normal tests because they require a webapp to be deployed to localhost (they test that webapp). The tests should be able to use classes defined in my main source set. How do I make this happen?
Update for 2021:
A lot has changed in 8ish years. Gradle continues to be a great tool. Now there's a whole section in the docs dedicated to configuring Integration Tests. I recommend you read the docs now.
Original Answer:
This took me a while to figure out and the online resources weren't great. So I wanted to document my solution.
This is a simple gradle build script that has an intTest source set in addition to the main and test source sets:
apply plugin: "java"
sourceSets {
// Note that just declaring this sourceset creates two configurations.
intTest {
java {
compileClasspath += main.output
runtimeClasspath += main.output
}
}
}
configurations {
intTestCompile.extendsFrom testCompile
intTestRuntime.extendsFrom testRuntime
}
task intTest(type:Test){
description = "Run integration tests (located in src/intTest/...)."
testClassesDir = project.sourceSets.intTest.output.classesDir
classpath = project.sourceSets.intTest.runtimeClasspath
}
Here is how I achieved this without using configurations{ }.
apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_6
sourceSets {
integrationTest {
java {
srcDir 'src/integrationtest/java'
}
resources {
srcDir 'src/integrationtest/resources'
}
compileClasspath += sourceSets.main.runtimeClasspath
}
}
task integrationTest(type: Test) {
description = "Runs Integration Tests"
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath += sourceSets.integrationTest.runtimeClasspath
}
Tested using: Gradle 1.4 and Gradle 1.6
This was once written for Gradle 2.x / 3.x in 2016 and is far outdated!!
Please have a look at the documented solutions in Gradle 4 and up
To sum up both old answers (get best and minimum viable of both worlds):
some warm words first:
first, we need to define the sourceSet:
sourceSets {
integrationTest
}
next we expand the sourceSet from test, therefor we use the test.runtimeClasspath (which includes all dependenciess from test AND test itself) as classpath for the derived sourceSet:
sourceSets {
integrationTest {
compileClasspath += sourceSets.test.runtimeClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath // ***)
}
}
note) somehow this redeclaration / extend for sourceSets.integrationTest.runtimeClasspath is needed, but should be irrelevant since runtimeClasspath always expands output + runtimeSourceSet, don't get it
we define a dedicated task for just running integration tests:
task integrationTest(type: Test) {
}
Configure the integrationTest test classes and classpaths use. The defaults from the java plugin use the test sourceSet
task integrationTest(type: Test) {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
(optional) auto run after test
integrationTest.dependsOn test
(optional) add dependency from check (so it always runs when build or check are executed)
tasks.check.dependsOn(tasks.integrationTest)
(optional) add java,resources to the sourceSet to support auto-detection and create these "partials" in your IDE. i.e. IntelliJ IDEA will auto create sourceSet directories java and resources for each set if it doesn't exist:
sourceSets {
integrationTest {
java
resources
}
}
tl;dr
apply plugin: 'java'
// apply the runtimeClasspath from "test" sourceSet to the new one
// to include any needed assets: test, main, test-dependencies and main-dependencies
sourceSets {
integrationTest {
// not necessary but nice for IDEa's
java
resources
compileClasspath += sourceSets.test.runtimeClasspath
// somehow this redeclaration is needed, but should be irrelevant
// since runtimeClasspath always expands compileClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath
}
}
// define custom test task for running integration tests
task integrationTest(type: Test) {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
tasks.integrationTest.dependsOn(tasks.test)
referring to:
gradle java chapter 45.7.1. Source set properties
gradle java chapter 45.7.3. Some source set examples
Unfortunatly, the example code on github.com/gradle/gradle/subprojects/docs/src/samples/java/customizedLayout/build.gradle or …/gradle/…/withIntegrationTests/build.gradle seems not to handle this or has a different / more complex / for me no clearer solution anyway!
The nebula-facet plugin eliminates the boilerplate:
apply plugin: 'nebula.facet'
facets {
integrationTest {
parentSourceSet = 'test'
}
}
For integration tests specifically, even this is done for you, just apply:
apply plugin: 'nebula.integtest'
The Gradle plugin portal links for each are:
nebula.facet
nebula.integtest
If you're using
Gradle 5.x, have a look at Documentation Section "Testing Java > Configuring integration tests
Example 14 and 15 for details (both for Groovy and Kotlin DSL, either which one you prefer)
alt: "current" Gradle doc link at 2, but might defer in future, you should have a look at if examples changes)
for Gradle 4 have a look at ancient version 3 which is close near to what #Spina posted in 2012
To get IntelliJ to recognize custom sourceset as test sources root:
plugin {
idea
}
idea {
module {
testSourceDirs = testSourceDirs + sourceSets["intTest"].allJava.srcDirs
testResourceDirs = testResourceDirs + sourceSets["intTest"].resources.srcDirs
}
}
Here's what works for me as of Gradle 4.0.
sourceSets {
integrationTest {
compileClasspath += sourceSets.test.compileClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath
}
}
task integrationTest(type: Test) {
description = "Runs the integration tests."
group = 'verification'
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
As of version 4.0, Gradle now uses separate classes directories for each language in a source set. So if your build script uses sourceSets.integrationTest.output.classesDir, you'll see the following deprecation warning.
Gradle now uses separate output directories for each JVM language, but this build assumes a single directory for all classes from a source set. This behaviour has been deprecated and is scheduled to be removed in Gradle 5.0
To get rid of this warning, just switch to sourceSets.integrationTest.output.classesDirs instead. For more information, see the Gradle 4.0 release notes.
I gather the documentation wasn't great back in 2012 when this question was asked, but for anyone reading this in 2020+: There's now a whole section in the docs about how to add a source set for integration tests. You really should read it instead of copy/pasting code snippets here and banging your head against the wall trying to figure out why an answer from 2012-2016 doesn't quite work.
The answer is most likely simple but more nuanced than you may think, and the exact code you'll need is likely to be different from the code I'll need. For example, do you want your integration tests to use the same dependencies as your unit tests?
You might also want to configure your tests to run with jUnit:
task integrationTest(type: Test) {
...
useJunitPlatform()
}
I'm new to Gradle, using Gradle 6.0.1 JUnit 4.12. Here's what I came up with to solve this problem.
apply plugin: 'java'
repositories { jcenter() }
dependencies {
testImplementation 'junit:junit:4.12'
}
sourceSets {
main {
java {
srcDirs = ['src']
}
}
test {
java {
srcDirs = ['tests']
}
}
}
Notice that the main source and test source is referenced separately, one under main and one under test.
The testImplementation item under dependencies is only used for compiling the source in test. If your main code actually had a dependency on JUnit, then you would also specify implementation under dependencies.
I had to specify the repositories section to get this to work, I doubt that is the best/only way.

Categories

Resources