Jacoco gradle multi module - java

We have a gradle project with mutliple modules - so each module - eg admin, ends up having something like this:
task admintest(type: Test) {
description = "Tests of admin"
testClassesDir = sourceSets.admintest.output.classesDir
classpath += sourceSets.admintest.runtimeClasspath
}
check.dependsOn admintest
now - we are generating a jacoco report - the report actually includes all the classes from all the different modules, but only has coverage from the tests that ran as part of the main module, ie the "test" task - while the admintests and others run, the coverage is always zero. How do I get jacoco to also pick up the coverage from the admintests, and other module tests?

Found it - for those who are trying to do this - the secret sauce is to specify the other modules test tasks in the executionData
eg:
jacocoTestReport {
executionData test, admintest, commontest
}

This is old thread , but this might help someone :
We only need this in build.gradle of root project.
Define a common location
Provide same to sonarqube as well.
plugins {
..
id "org.sonarqube"
}
apply plugin: 'org.sonarqube'
allprojects {
apply plugin: 'jacoco'
}
task jacocoRootReport(type: JacocoReport) {
dependsOn = subprojects.test
description = 'Generates aggregate report from all subprojects.'
.....
reports {
xml.enabled true
xml.destination(file("${jacoco.reportsDir}/all-tests/jacocoRootReport.xml"))
csv.enabled false
}
}
sonarqube {
properties {
property "sonar.coverage.jacoco.xmlReportPaths", jacocoRootReport.reports.xml.destination
}
}

Related

Spring Boot buildInfo is causing Groovy Source Set be not "up-to-date" all the time

in our project we are working with
springBoot 2.2.11
groovy 2.5.6
Our build.gradle looks like the following:
...
plugins {
id 'groovy'
id 'java'
id 'idea'
}
configurations {
testCompile.extendsFrom compile
testRuntime.extendsFrom runtime
...
}
...
sourceSets {
test {
java { srcDirs = ['src/test/java'] }
groovy {srcDirs = ['src/test/groovy'] }
}
...
}
...
springBoot {
buildInfo()
}
...
When we run gradlew test --info we get the following output:
...
> Task :bootBuildInfo
Caching disabled for task ':bootBuildInfo' because:
Build cache is disabled
Task ':bootBuildInfo' is not up-to-date because:
Value of input property 'properties.time' has changed for task ':bootBuildInfo'
...
> Task :compileJava UP-TO-DATE
...
Skipping task ':compileJava' as it is up-to-date.
...
> Task :compileTestGroovy
Caching disabled for task ':compileTestGroovy' because:
Build cache is disabled
Task ':compileTestGroovy' is not up-to-date because:
Input property 'astTransformationClasspath' file C:\projects\test-project\build\resources\main\META-INF\build-info.properties has changed.
The input changes require a full rebuild for incremental task ':compileTestGroovy'.
...
The build-info.properties file which is generated by spring-boots buildInfo step contains a build.time property which is updated on every execution.
The Java Compiler excludes this file apperently as the up-to-date check for :compileJava is returning true. However, the compileGroovy tasks includes this file into his sourceSet which is why it returns false on the up-to-date check.
I already tried the exclude option as follows with no success.
sourceSets {
test {
java { srcDirs = ['src/test/java'] }
groovy {
srcDirs = ['src/test/groovy']
excludes = [file("${buildDir}/resources/main/META-INF/build-info.properties")]
}
}
}
Any ideas on how to fix this? Idealy I would like to exclude the file build\resources\main\META-INF\build-info.properties from the up-to-date-check (or even the input sourceSet of compileGroovy).
You need to make spring-boot.properties generated with no time part(what makes springBoot task out of dated what in turn leads compileJava outdated)
springBoot {
buildInfo {
properties {
// to make compileJava up to date
// https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#integrating-with-actuator.build-info
time = null
}
}
}

Aggregate several JaCoCo .exec files into a single Coverage report with Gradle

Situation
I'm working on a Spring Boot application that is coming along nicely, but the build-test cycle takes too long to be practical in a plain Dev environment.
So, I recently decided to split-up my unit-tests and integration tests. The integration tests are based on Cucumber whereas the unit tests are plain JUnit 5 tests.
For this I used the Gradle plugin org.unbroken-dome.test-sets which nicely creates the relevant sourceSets etc.
testSets {
intTest { dirName = 'int-test' }
}
Everything is working nicely and my tests are executing as intended. The unit tests are still executed when I do a build and the integration tests are executed when I call `gradlew intTest'. This is by design, as I don't want to execute all tests all the time.
After running the tests, I also have a jacoco/test.exec file and a jacoco/inttest.exec file. All by design and as advertised by the test-sets plugin.
Problem
Here's my problem. When I had all my tests still in the test-SourceSet, so before the split and with only the test.exec file generated, JaCoCo reported a coverage of around 75%. But now that I have split up the tests and having the test.exec and intTest.exec files, JaCoCo only reports a coverage of 15%, which is about right considering the extent of my unit tests and integration tests.
Question
How can I get JaCoCo to use both the test.exec and the intTest.exec files to report the coverage and allow for the coverage verification task to consider both again?
Relevant Code
Here is some of the relevant code:
jacoco.gradle
apply plugin: 'java'
apply plugin: 'jacoco'
// -----------------------------------------------------------
//
// JaCoCo
dependencies {
implementation "org.jacoco:org.jacoco.agent:${jacocoToolVersion}:runtime"
}
jacoco {
toolVersion = "${jacocoToolVersion}"
}
jacocoTestReport {
dependsOn test
reports {
xml.enabled false
csv.enabled true
html.destination file("${buildDir}/reports/jacoco/Html")
csv.destination file("${buildDir}/reports/jacoco/jacoco.csv")
}
}
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = minCodeCoverage
}
}
}
}
check.dependsOn jacocoTestCoverageVerification
Snippet from build.gradle:
tasks.withType(Test) {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
testLogging.showStandardStreams = true
reports {
junitXml.enabled = true
html.enabled = true
}
}
and
testSets {
intTest { dirName = 'int-test' }
}
intTest.mustRunAfter test
check.dependsOn intTest
test {
systemProperties System.getProperties()
finalizedBy jacocoTestReport
}
Thanks in advance for helping me out or pointing me into the right direction.
Okay, you know that once you've articulated your question, you know what to Google? Well after some more digging around I got some inspiratipon that gave me this as a jacocoTestReport definition:
jacocoTestReport {
dependsOn test
sourceSets sourceSets.main
executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")
reports {
xml.enabled false
csv.enabled true
html.destination file("${buildDir}/reports/jacoco/Html")
csv.destination file("${buildDir}/reports/jacoco/jacoco.csv")
}
}
The mistake I had made was in that I had the exec files aggregated in the jacocoTestCoverageVerification section, instead of the jacocoReport.
So, putting the executionData part in the right place, gave me the correct coverage values again. Yoohoo!!!

How to generate cucumber html report with attached failed screenshot and cucumber.json file using gradle based project

Using gradle, I am trying generate cucumber html report with failed screenshot attached to it for security reason I cannot have online plugins in build.gradle file so I have to download required jar and plugins and implement and configure library manually in build.gradle file.
Please suggest how can configure TestRunner file in build.gradle and generate cucumber html report with cucumber.json file
build.gradle file
plugins {
id 'java'
id 'idea'
}
group 'org.example'
version '1.0-SNAPSHOT'
configurations {
cucumberRuntime.extendsFrom testRuntime
}
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "io.cucumber.api.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty', '--glue', 'stepDef', 'src/test/java']
}
}
}
repositories {
mavenCentral()
}
dependencies {
implementation fileTree(dir:System.getProperty("user.dir")+'/Plugin',include:['*.jar'])
implementation files('junit-4.12')
implementation files('testng-6.7.jar')
implementation files('junit-jupiter-api-5.6.2')
implementation files('hamcrest-all-1.3')
.....................
TestRunner file
package TestRunner;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features = "src/test/resources",
glue = "StepDefs",
plugin = {
"pretty", "html:target/cucumber-html-report", "json:target/cucumber.json", "pretty:target/cucumber-pretty.txt"
}
)
public class TestRunner {
}
Whatever StepDefs may be ...
Running with gradle cucumber --info might be useful for debugging... because the error message finished with non-zero exit value 1 just indicates "error" or "no success".
You'd probably need these Java dependencies, to begin with:
testImplementation 'io.cucumber:cucumber-java:6.5.0'
testImplementation 'io.cucumber:cucumber-junit:6.5.0'
And one might have to add gradle.cucumber as the --glue into the arguments args, as the documentation suggests. Task dependency compileTestJava should rather be testClasses.
html generally is a plugin, which expects an output directory, therefore this should look alike this:
task cucumber() {
dependsOn assemble, testClasses
doFirst {
}
doLast {
javaexec {
main = 'io.cucumber.core.cli.Main'
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = [
'--plugin', 'pretty', 'html:target/reports',
'--glue', 'gradle.cucumber',
'src/test/resources'
]
}
}
}
These args can also be annotated in Java; not sure which of them takes precedence.It probably makes no sense and only creates a mess, when defining the arguments twice.
Make sure to follow instruction #4:
Add feature .feature files and associated step mapping classes .java in src/test/resources and src/test/java respectively in a gradle.cucumber package.
-g, --glue PATH Where glue code (step definitions, hooks and plugins) are loaded from.
When running with jUnit, one can also pass options with a junit-platform.properties file.
The most easy might be to start with the cucumber-java-skeleton (it is known to be working).
It didn't work for me, If I run this cucumber task it gives me error
Task :cucumber FAILED
Error: Could not find or load main class io.cucumber.api.cli.Main
Caused by: java.lang.ClassNotFoundException: io.cucumber.api.cli.Main
Error: Could not find or load main class io.cucumber.api.cli.Main
I have created one task cucumberRunner which executes the TestRunner.java file, it is creating cucumber.json file and html report but htlm
report but HTML report is not expected is weird no graphics and colorless colorless
build.gradle I'm using:
```
configurations {
cucumberRuntime {
extendsFrom testRuntime
}
}
task cucumber() {
dependsOn assemble, testClasses
doFirst {
}
doLast {
javaexec {
main = 'io.cucumber.api.cli.Main' // tried with io.cucumber.core.cli.Main
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = [
'--plugin', 'pretty', 'html:target/reports',
'--glue', 'gradle.cucumber',
'src/test/resources'
]
}
}
}
task cucumberRunner(type: Test) {
include '**/**TestRunner.class'
}
Also I have added jars
implementation files('junit-4.12')
implementation files('testng-6.0.jar')
implementation files('cucumber-core-6.0.0')
implementation files('cucumber-java-6.0.0')
implementation files('cucumber-plugin-6.0.0')
implementation files('cucumber-junit-6.0.0')
implementation files('cucumber-testng-6.0.0')
implementation files('cucumber-jvm-deps-1.0.5')
implementation files('cucumber-gherkin-6.0.0')
implementation files('cucumber-java8-6.0.0')
implementation files('cucumber-html-0.2.3')
```

Gradle: Task with path not found in project

I have a gradle project with the following structure:
rootDir
|--agent-v1.0.0
|--agent.jar
|--proj1
|-- // other project files
|--build.gradle
|--proj2
|-- // other project files
|--build.gradle
|--build.gradle
I would like to run test.jvmArgs = ['javaagent:agent-v1.0.0/agent.jar'] for all subprojects, so I wrote the following task in the root build.gradle:
subprojects {
task cs {
outputs.upToDateWhen { false }
dependsOn test.jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar']
}
}
But this fails with:
Could not determine the dependencies of task ':proj1'.
Task with path 'javaagent:../agent-v1.0.0/agent.jar' not found in project ':proj1'.
I've tried this by putting the agent-v1.0.0 in both the root, and in each project, and it still fails. What am I missing?
Why are you wrapping that logic in a new task? And then passing the return from jvmArgs to dependsOn?
Just configure the test tasks correctly:
subprojects {
tasks.withType(Test) {
jvmArgs "-javaagent:${project.rootDir}/agent-v1.0.0/agent.jar"
}
}
A task can depend on another task. So dependsOn expects a task as argument. test.jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar'] is not a task.
If you want to configure all the test tasks of all subprojects to have additional jvm args, then the syntax would be
subprojects {
// this block of code runs for every subproject
afterEvaluate {
// this block of code runs after the subproject has been evaluated, and thus after
// the test task has been added by the subproject build script
test {
// this block of code is used to configure the test task of the subproject
// this configures the jvmArgs property of the test task
jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar']
}
}
}
But just don't copy and paste this code. Read the grade user guide, and learn its fundamental concepts.

the stuff of "compile project" can't be carried into the aar when publish to maven reporsitory

When I compile the DemoProject:demo with dependencies compile 'cn.dreamtobe.android.phone:sdkproject-library:1.0.1, it failed with following error tips:
Download http://mymaven.dreamtobe.cn:8081/artifactory/content/repositories/mobile/cn/dreamtobe/android/phone/sdkproject-library/1.0.0/sdkproject-library-1.0.1.pom
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':demo'.
> Could not resolve all dependencies for configuration ':demo:_debugCompile'.
> Could not find SDKProject:api:unspecified.
Searched in the following locations:
file:/Users/Jacksgong/.m2/repository/SDKProject/api/unspecified/api-unspecified.pom
file:/Users/Jacksgong/.m2/repository/SDKProject/api/unspecified/api-unspecified.jar
https://jcenter.bintray.com/SDKProject/api/unspecified/api-unspecified.pom
https://jcenter.bintray.com/SDKProject/api/unspecified/api-unspecified.jar
Required by:
DemoProject:demo:unspecified > cn.dreamtobe.android.phone:sdkproject-library:1.0.1
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 42.323 secs
So the real Question is:
How to carry the stuff of :api module together rather than just declare it in the .pom file when publishing the :libray module to the maven repo.
since we can't find it in DemoProject, because of ':api' just can be recognized in the SDKProject
My SDKProject hierarchy is:
SDKProject
|- library module(`:library`)
|- api module(`:api`)
and library-module dependent the api-module, bellow is build.gradle in the library-module
...
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile project(':api')
}
ext {
GROUP_ID = GROUP
POM_ARTIFACT_ID = IMPL_POM_ARTIFACT
}
apply from: '../upload.gradle'
the upload.gradle the script in the SDKProject root directory:
apply plugin: 'maven'
ext {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
MVN_NAME = properties.getProperty("mvn.dev.user")
MVN_PASSWORD = properties.getProperty("mvn.dev.password")
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = GROUP_ID
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: RELEASE_REPOSITORY_URL) {
authentication(userName: MVN_NAME, password: MVN_PASSWORD)
}
snapshotRepository(url: SNAPSHOT_REPOSITORY_URL) {
authentication(userName: MVN_NAME, password: MVN_PASSWORD)
}
}
}
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
failOnError false
source = android.sourceSets.main.java.srcDirs
options {
charSet = 'UTF-8'
links "http://docs.oracle.com/javase/7/docs/api/"
linksOffline "http://d.android.com/reference", System.getenv("ANDROID_HOME") + "/docs/reference"
}
classpath += project.android.libraryVariants.toList().first().javaCompile.classpath
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
}
I solved this issue by myself through flat-aar.
And the another way is put Apis code and Impls code in the same module, and write gradle |ike this : publishing multiple modules to maven, In this way, gradle script will filter out Api and Impl as two module.
Finally
I create two sample demonstate for this issue: https://github.com/Jacksgong/gralde-sample

Categories

Resources