I have a java gradle project with a custom sourceset “src/integration-test” next to the standard “src/test” folder.
build.gradle
plugins {
...
id 'org.sonarqube' version '3.3'
id 'jacoco'
}
sourceSets {
integrationTest {
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/java')
}
resources.srcDir file('src/integration-test/resources')
}
}
jacocoTestReport {
getExecutionData().setFrom(fileTree(buildDir).include("/jacoco/*.exec"))
reports {
xml.required = true
}
}
sonarqube {
properties {
properties["sonar.tests"] += sourceSets.integrationTest.allSource.srcDirs.findAll({ it.exists() })
properties["sonar.coverage.jacoco.xmlReportPaths"] = "${project.buildDir}/reports/jacoco/test/jacocoTestReport.xml"
}
}
...
I’ve tried several things but Sonarqube still only picks up the unit tests in “src/test” and ignoring the ones in my custom sourceset.
Versions:
Gradle v7.3
Sonarqube v3.3
Java 17
What am I doing wrong?
Thanks.
Related
when trying to upgrade my gradle version from 5.x to 7.x, getting below error
* What went wrong:
Configuration with name 'integrationTestCompile' not found.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.
Below is my code
sourceSets {
create("integrationTest") {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.test.get().output
}
}
val integrationTestCompile: Configuration by configurations.getting {
extendsFrom(configurations.testImplementation.get())
}
val integrationTestRuntimeOnly: Configuration by configurations.getting {
extendsFrom(configurations.testRuntimeOnly.get())
}
tasks {
val integrationTest by creating(Test::class) {
description = "Running integration tests."
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
shouldRunAfter("test")
}
}
Using gradle with kotlin.
How to fix this?
In below way you can create integrationTest task in gradle latest version
configurations {
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom runtimeOnly
}
sourceSets {
integrationTest {
compileClasspath += sourceSets.main.output
compileClasspath += configurations['testRuntimeClasspath']
runtimeClasspath += compileClasspath
}
}
task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
mustRunAfter test
}
I have into the same problem quite a lot of people have here, which is getting proper code coverage information when using Jacoco/Gradle and Powermock.
I have read all the various threads here and in other places and I have successfully managed to create a task (for Gradle 6.4) that does offline instrumentation of my project's classes. For reference the code that does this is the following:
task instrumentClasses(dependsOn: [ classes, project.configurations.jacocoAnt ]) {
inputs.files classes.outputs.files
File outputDir = new File(project.buildDir, 'instrumented')
outputs.dir outputDir
doFirst {
project.delete(outputDir)
ant.taskdef(
resource: 'org/jacoco/ant/antlib.xml',
classpath: project.configurations.jacocoAnt.asPath,
uri: 'jacoco'
)
def instrumented = false
jacocoOfflineSourceSets.each { sourceSetName ->
if (file(sourceSets[sourceSetName as String].output.classesDirs.singleFile.absolutePath).exists()) {
def instrumentedClassedDir = "${outputDir}/${sourceSetName}"
ant.'jacoco:instrument'(destdir: instrumentedClassedDir) {
fileset(dir: sourceSets[sourceSetName as String].output.classesDirs.singleFile, includes: '**/*.class')
}
//Replace the classes dir in the test classpath with the instrumented one
sourceSets.test.runtimeClasspath -= sourceSets[sourceSetName as String].output.classesDirs
sourceSets.test.runtimeClasspath += files(instrumentedClassedDir)
instrumented = true
}
}
if (instrumented) {
//Disable class verification based on https://github.com/jayway/powermock/issues/375
test.jvmArgs += '-noverify'
}
}
}
Now, for the most part this seems to work alright. I have successfully verified that my classes are now properly instrumented and I'm seeing a Jacoco produced report which has the correct information. Problem is though that my SonarQube server still lists the classes in question as non covered. Regarding this I have no idea as to what I need to do to resolve it.
For reference I am using the following version of the sonarqube plugin:
"org.sonarqube" version "2.7"
And my CI runs the Gradle task in the following manner:
- ./gradlew jacocoTestReport sonarqube ${SONAR_GRADLE_EXTRA_PARAMS} -Dsonar.projectKey=${CI_PROJECT_ID} -Dsonar.host.url=${SONAR_URL} -Dsonar.login=${SONAR_LOGIN} -Dsonar.branch.name=${CI_COMMIT_REF_NAME};
I do get that it must be some configuration issue with either SonarQube or the way I run the Gradle task but I am not really sure as to what is the culprit.
If you are able to generate the aggregated jacoco report (aggregated from all source sets), then you can simply specify that in your sonarqube task while running (and sonar will just pick the exact coverage info that jacoco calculated)
./gradlew sonarqube -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=XXXX -Dsonar.organization=XXXXX -Dsonar.coverage.jacoco.xmlReportPaths=build/jacoco-report.xml
FYI I am creating the aggregated report at build/jacoco-report.xml
Below is my gradle configuration (might be useful for you)
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
id 'jacoco'
id "org.sonarqube" version "2.8"
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
sourceSets {
intTest {
compileClasspath += sourceSets.main.output + sourceSets.test.output
runtimeClasspath += sourceSets.main.output + sourceSets.test.output
}
}
configurations {
intTestImplementation.extendsFrom testImplementation
intTestRuntimeOnly.extendsFrom testRuntimeOnly
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
testLogging.showStandardStreams = true //To print logs
}
task integrationTest(type: Test) {
testClassesDirs = sourceSets.intTest.output.classesDirs
classpath = sourceSets.intTest.runtimeClasspath
shouldRunAfter test
testLogging.showStandardStreams = true //To print logs
}
jacocoTestReport {
executionData(file("${project.buildDir}/jacoco/test.exec"), file("${project.buildDir}/jacoco/integrationTest.exec"))
reports {
xml.enabled true
csv.enabled false
xml.destination file("${buildDir}/jacoco-report.xml")
html.destination file("${buildDir}/jacocoHtml")
}
mustRunAfter(test, integrationTest) // integration tests are required to run before generating the report
}
jacocoTestCoverageVerification {
executionData(file("${project.buildDir}/jacoco/test.exec"), file("${project.buildDir}/jacoco/integrationTest.exec"))
violationRules {
rule {
limit {
counter = 'INSTRUCTION'
minimum = 0.94
}
limit {
counter = 'BRANCH'
minimum = 1.0
}
}
}
}
check.dependsOn(integrationTest, jacocoTestCoverageVerification)
tasks.withType(Test) {
finalizedBy jacocoTestReport
}
I am currently working on a Vaadin 8 project using the gradle vaadin-plugin.
In the project there are 3 SourceSets main,a and b. The later two exclude some views and features. Is it possible to build 3 Artifcats via gradle one for each sourceSet. I tried modifying the war task but that broke the views.
conf{
aCompile.extendsFrom compile
aRuntime.extendsFrom runtime
bCompile.extendsFrom compile
bRuntime.extendsFrom runtime
}
sourceSets {
main {
java {
srcDirs = ['src/main/java', 'src/main/generated']
}
}
a{
java {
srcDirs = ['src/main/java', 'src/main/generated']
exclude 'com/exmaple/features/b'
exclude 'com/exmaple/views/b'
}
compileClasspath += main.output
runtimeClasspath += main.output
output.resourcesDir = 'build/resources/main'
output.classesDir = 'build/classes/java/main'
}
b{
java {
srcDirs = ['src/main/java', 'src/main/generated']
exclude 'com/exmaple/features/a'
exclude 'com/exmaple/views/a'
}
compileClasspath += main.output
runtimeClasspath += main.output
output.resourcesDir = 'build/resources/main'
output.classesDir = 'build/classes/java/main'
}
}
task aWar(type: War) {
appendix = "a"
from sourceSets.a.output
}
task bWar(type: War) {
appendix = "b"
from sourceSets.b.output
}
From https://docs.gradle.org/current/userguide/artifact_management.html#sec:declaring_artifacts you should be able to declare an artifact for every source set.
In your case it would look something like this:
task aWar(type: War, dependsOn: classes) {
baseName = 'a.war'
classpath = project.configurations.aRuntime
}
task bWar(type: War, dependsOn: classes) {
baseName = 'b.war'
classpath = project.configurations.bRuntime
}
artifacts {
archives aWar, bWar
}
The WAR task also allows you to include/exclude classes using a regexp. Checkout more at https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.War.html
I am trying to run my junit tests through gradle file but build gets successful but does not run any tests. This is how my gradle file looks like:
apply plugin: 'java'
// Creating a new sourceSet because you should move your integration tests to a separate directory.
sourceSets {
test {
java.srcDirs = ['src/integration-test/java']
}
integrationTest {
java.srcDirs = ['src/integration-test/java']
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
task integrationTest(type: Test, description: 'Runs the integration tests', group: 'Verification') {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
After running task integrationTest, build runs successfully but it does not run any tests. Does anyone know why?
Don't know what exactly you want to do: separate your integration tests of fully move test classes to other folder but I think you need to include the dependencies of test configuration like this:
configurations {
integrationCompile.extendsFrom testCompile
integrationRuntime.extendsFrom testRuntime
}
For example integration test configuration:
sourceSets {
test {
java.srcDirs = ['src/test/java']
}
integration {
java.srcDirs = ['src/integration/java']
resources.srcDir 'src/integration/resources'
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
}
}
configurations {
integrationCompile.extendsFrom testCompile
integrationRuntime.extendsFrom testRuntime
}
task integration(type: Test, group: 'Verification') {
testClassesDir = sourceSets.integration.output.classesDir
classpath = sourceSets.integration.runtimeClasspath
}
In case if you just want to move your tests in other folder(src/integration-test/java) and run them with test task you can use the following configuration:
sourceSets {
test {
java.srcDirs = ['src/integration-test/java']
}
}
I have a project with the java plugin applied, and an additionnal source set
project(':dependencie') {
apply plugin: 'java'
sourceSets {
generated {
java {
srcDir 'src/generated/java'
}
}
main {
compileClasspath += generated.output
runtimeClasspath += generated.output
}
test {
compileClasspath += generated.output
runtimeClasspath += generated.output
}
}
}
And another depending of this one :
project(':dependsFrom') {
apply plugin: 'java'
dependencies {
compile(
project(':dependencie')
) { transitive = false }
}
}
My problemes is that when I am trying to compile dependsFrom, it doesn't find the classes of the sourceSet generated for the project dependencie. My workaround is to add the line
output.dir(generated.output, builtBy: 'regenerateFromWsdl')
to the main source set of dependencie but any insight to how "registering" the output of the generated source set for future dependencies would be really helpful.
I you want to model the generated code as a separate source set, you have to create a Jar and publish it via the runtime configuration:
task generatedJar(type: Jar) {
from sourceSets.generated.output
}
artifacts {
runtime generatedJar
}
Alternatively, you could add src/generated/java as another source directory for the main source set, in which case it will get compiled/packaged/exported together with that.
In fact I found a solution. You just need to add the generated sourceSet output to the jar task of dependencie project, as it is this same jar task which "inject" the project into the dependecie system.
My problem was that I tought that any additional sourceSet of the project would have been automatically used by all the tasks of the project.
Here the modified gradle script
project(':dependencie') {
apply plugin: 'java'
sourceSets {
generated {
java {
srcDir 'src/generated/java'
}
}
main {
compileClasspath += generated.output
runtimeClasspath += generated.output
}
test {
compileClasspath += generated.output
runtimeClasspath += generated.output
}
}
jar {
from sourceSets.generated.output
}
}