How to create an HTML report for JUnit 5 tests? - gradle

Is there already a possibility to generate an HTML report when JUnit tests were started via Gradle? Any hint or comment is appreciated.

UPDATE
Gradle 4.6 provides built-in support for the JUnit Platform which allows you to run JUnit Jupiter tests using the standard Gradle test task which generates HTML reports out of the box.
Answer for Gradle versions prior to 4.6
The JUnit Platform Gradle Plugin generates JUnit 4 style XML test reports.
These XML files are output to build/test-results/junit-platform by default.
So, if your build server knows how to parse JUnit 4 style XML reports, you can just point it to the XML files in that directory and let the build server generate the HTML report for you.
However, if you are asking if Gradle can generate an HTML report for your tests run via the junitPlatformTest task, then the answer is "No, unfortunately not." The reason is that the standard Gradle test task only generates HTML reports based on its own proprietary "binary" report format. Since the junitPlatformTest task does not generate reports in Gradle's binary format, Gradle itself cannot generate HTML reports for JUnit Platform tests.
Having said that, however, there is in fact a work around: you can use Ant within your Gradle build. Ant has a task for aggregating JUnit 4 based XML reports and generating an HTML report from those aggregated reports. The output is not very modern, but it is at least human readable. The downside is that the default XSLT stylesheet does not display the test class names for tests run via the JUnit Platform.
In any case, you can configure Ant's JUnitReport task in Gradle as follows.
junitPlatform {
// configure as normal
}
configurations {
junitXmlToHtml
}
task generateHtmlTestReports << {
def reportsDir = new File(buildDir, 'test-reports')
reportsDir.mkdirs()
ant.taskdef(
name: 'junitReport',
classname: 'org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator',
classpath: configurations.junitXmlToHtml.asPath
)
ant.junitReport(todir: "$buildDir/test-results/junit-platform", tofile: "aggregated-test-results.xml") {
fileset(dir: "$buildDir/test-results/junit-platform")
report(format: 'frames', todir: reportsDir)
}
}
afterEvaluate {
def junitPlatformTestTask = tasks.getByName('junitPlatformTest')
generateHtmlTestReports.dependsOn(junitPlatformTestTask)
check.dependsOn(generateHtmlTestReports)
}
dependencies {
// configure as normal ...
junitXmlToHtml 'org.apache.ant:ant-junit:1.9.7'
}
Then, executing gradle check will generate an HTML report in build/test-reports/index.html.
Regards,
Sam (Core JUnit 5 committer)

Adding below line to my java command created TEST-junit-jupiter.xml in my target/test-result folder. This xml file has all info about number of testcases run, number of tests passed/failed etc
--reports-dir target/test-result

Yes, you can using Jacoco plugin.
Here is an example:
apply plugin: 'war' or apply plugin: 'java'
apply plugin: "jacoco"
test {
reports.junitXml.destination="build/test-results"
jacoco {
destinationFile = file("build/jacoco/jacoco.exec")
append=true
}
}
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/jacocoHtml"
}
}
Regards.

Related

jacoco xml report doesn't hava overall covered percentage where as html file has

When I generate the code coerage report using jacoco plugin using gradle task. The html file has percentage of code that's covered. But the xml file dosn't have any percentage.
The gradle task I have in the .gradle file.
jacocoTestReport {
dependsOn test // tests are required to run before generating the report
reports {
xml.enabled = true
html.enabled = true
}
}

how to let jacoco know sources/classes are outside the module

Module A is a separate module having all the integration tests. Maven-Jacoco-Sonar works fine, if sources and tests are located at one module.
Since module A is a different module altogether, we couldn't be able to generate the jacoco reports easily. Because, jacoco is unable to find the sources within module A to measure the coverage for report. Source files are all residing at different modules. jacoco needs to have the class files and source files available in order to generate a report. We need to link up them to module A accordingly while generating the jacoco report so that, jacoco-it.exec can be easily pulled to Sonar to display the coverage analysis.
Currently in this case,
Classes triggered according to the jacoco agent but no source/class files available
Please help me if anyone has as solution how to achieve the above functionality. It would be so helpful to me.
try adding the following as your main src
def debugTree = [fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter),
fileTree(dir: "${project(":your_module_name").projectDir}/build/intermediates/classes/debug", excludes: fileFilter)]
def mainsrc = [fileTree(dir:"${project(":your_module_name").projectDir}/src/main/java"),
fileTree(dir:"${project.projectDir}/src/main/java")]
sourceDirectories = files(mainsrc)
classDirectories = files(debugTree)
This will include your external sub module files to be used while creating the jacoco report.
Try setting sourcesets variable.
jacocoTestReport {
sourceSets project(':module_name').sourceSets.main
reports {
xml.enabled true
// enable html report for debugging in local env
// html.enabled true
// html.destination = file("${buildDir}/jacocoHtml")
}
}

How to aggregate junit test suite results into a single report run by gradle?

I have a junit test suite that runs 4 test classes. When I run the test suite using gradle it creates 4 html reports, 1 for each test class in the suite. I'm new to gradle, is there a way to have gradle combine the results into a single html report?
Here is my test suite.
#RunWith(Suite.class)
#Suite.SuiteClasses([
TestClass1.class,
TestClass2.class,
TestClass3.class,
TestClass4.class,
])
class MyTestSuite {
}
In my gradle.build file I the following test method.
test {
include("com/geb/tests/MyTestSuite.class")
jvmArgs '-Dsomevariabe=someValue'
}
I run my test suite with the gradle command: gradle :web-tests:test
Then when the test suite has completed running there are 4 html files created for each class. Saved in web-tests\build\reports\tests\classes
testClass1.html
testClass2.html
testClass3.html
testClass4.html
Each testClass.html file lists each test cases pass or fail status.
I'd like to have a single html file containing a combined list of all the test cases pass or fail statuses. Is this possible?
This plugin can help you. Add this to your build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.kncept.junit.reporter:junit-reporter:2.1.0'
}
}
plugins {
id 'com.kncept.junit.reporter' version '2.1.0'
}
apply plugin: 'com.kncept.junit.reporter'
junitHtmlReport {
// The maximum depth to traverse from the results dir.
// Any eligible reports will be included
maxDepth = 5
// Directory where to search (exact or relative to build path)
testResultsDir = '/path/to/root/of/project'
// Where to output
testReportsDir = 'reports/junit'
// Fail build when no XML files to process
failOnEmpty = true
}
Now build the report with:
./gradlew junitHtmlReport
Creates an HTML report of all JUnit XML files it finds to ./reports/junit. Note that this combines files like TEST-*.xml and not the html files.

jacoco+gradle taskdef A class needed by class org.jacoco.ant.InstrumentTask canno t be found

I'm trying to get jacoco+gradle work together. Looking at this forum it seems some people have successfully done that. But when I tried I got a strange exception.
What I did:
1.Downloaded gradle 2.2.1 and configured env variables, etc.
2.Downloaded jacoco 0.7.1 from http://www.eclemma.org/jacoco/
3.added:
apply plugin: 'jacoco'
and
buildTypes {
debug
{
testCoverageEnabled true
...
in the build.gradle
4.run gradle build
5.I got an error saying the jacoco agent jar cannot be found, etc. Error message showed it tried to search files under C:\Program Files (x86)\Android\android-sdk\extras\android\m2repository... etc.
6.I manually unzipped the jacoco jar files and put them in the place where the error message mentioned and the error message disappeared.
7.Then I ran gradle build. I got the following new error when running the built-in instrumentDebug task:
Caused by: : taskdef A class needed by class org.jacoco.ant.InstrumentTask cannot be found: org/jacoco/core/runtime/IExecutionDataAccessorGenerator using the classloader AntClassLoader[C:\Program Files (x86)\Android\android-sdk\extras\android\m2repository\org\jacoco\org.jacoco.ant\0.7.1.201405082137\org.jacoco.ant-0.7.1.201405082137.jar]
at org.apache.tools.ant.taskdefs.Definer.addDefinition(Definer.java:612)
at org.apache.tools.ant.taskdefs.Definer.execute(Definer.java:237)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:292)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.gradle.api.internal.project.ant.BasicAntBuilder.nodeCompleted(BasicAntBuilder.java:77)
at org.gradle.api.internal.project.ant.BasicAntBuilder.doInvokeMethod(BasicAntBuilder.java:92)
at com.android.build.gradle.internal.coverage.JacocoInstrumentTask.instrument(JacocoInstrumentask.groovy:51)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:63)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:218)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:211)
Caused by: java.lang.ClassNotFoundException:
at org.jacoco.core.runtime.IExecutionDataAccessorGenerator
at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1366)
at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1315)
at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1068)...
When I used process monitor (My dev machine is Win 7), I saw there was no access to org.jacoco.core-0.7.1.201405082137.jar,which the org.jacoco.ant.InstrumentTask class resides in. So I think the gradle did not pass the file path to org.apache.tools.ant.AntClassLoader correctly.
I have tried below ways but none of them worked:
Add all jacoco jar files to CLASSPATH environment variable.
Add the jacoco jar files to ant libs, gradle libs,gradle libs/plugins, folder.
Look at source code of gradle 2.2.1. Clueless at this moment...
Does anyone know how to resolve this problem?
Thanks in advance!
Edit:
The main purpose is to:
1. Let jacoco build an instrumented apk file
2. Manually test the apk file (Not auto test)
3. Let jacoco generate coverage report
Update:
I just found when executing InstrumentDebug task, the gradle starts a new process with below command:
"C:\Program Files\Java\jdk1.8.0_25\bin\java.exe" -XX:MaxPermSize=512m -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country=CN -Duser.language=zh -Duser.variant -cp D:\gradle-2.2.1\gradle-2.2.1\lib\gradle-launcher-2.2.1.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 2.2.1 D:\gradle-2.2.1\gradle-2.2.1\daemon 120000 744501ac-32c1-4930-82bd-59e0a9e2b92d -XX:MaxPermSize=512m -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country=CN -Duser.language=zh -Duser.variant
As you can see the class path is hard coded, which overwrites what defined in environment variable. So the required core lib of jacoco could not be found and thus caused this problem. I'm now trying to see where this process is started and whether there is a way to change the -cp option.
Update2:
I finally found this process is started by gradle-launcher-2.2.1.jar. See below code. I'm now trying to see whether it's possible to change DefaultModuleRegistry and make registry.getGradleHome() == null. In the meantime could anyone who has successfully used jacoco+gradle tell me which version of gradle are you using?
public DaemonStartupInfo startDaemon()
{
DefaultModuleRegistry registry = new DefaultModuleRegistry();
Set<File> bootstrapClasspath = new LinkedHashSet();
bootstrapClasspath.addAll(registry.getModule("gradle-launcher").getImplementationClasspath().getAsFiles());
if (registry.getGradleHome() == null)
{
bootstrapClasspath.addAll(registry.getFullClasspath());
}
if (bootstrapClasspath.isEmpty()) {
throw new IllegalStateException("Unable to construct a bootstrap classpath when starting the daemon");
}
new JvmVersionValidator().validate(this.daemonParameters);
List<String> daemonArgs = new ArrayList();
daemonArgs.add(this.daemonParameters.getEffectiveJavaExecutable());
List<String> daemonOpts = this.daemonParameters.getEffectiveJvmArgs();
LOGGER.debug("Using daemon opts: {}", daemonOpts);
daemonArgs.addAll(daemonOpts);
daemonArgs.add("-cp");
daemonArgs.add(CollectionUtils.join(File.pathSeparator, bootstrapClasspath));
daemonArgs.add(GradleDaemon.class.getName());
daemonArgs.add(GradleVersion.current().getVersion());
daemonArgs.add(this.daemonDir.getBaseDir().getAbsolutePath());
daemonArgs.add(String.valueOf(this.daemonParameters.getIdleTimeout()));
daemonArgs.add(this.daemonParameters.getUid());
...
}
See if this helps.
I'm not using Gradle 2.2.1 but this is what I have in my Global Gradle file (i.e. a file in $GRADLE_HOME/init.d level file). File name can be anything with .gradle extension.
allprojects {
apply plugin: 'java'
apply plugin: 'pmd'
apply plugin: 'findbugs'
apply plugin: 'checkstyle'
apply plugin: 'jacoco'
//NOTE1: The following soureSet section is NOT required, if your folder structure follows what Gradle says where your main source should reside, where test (Unit tests) should reside, and where other like integrationTest (integration tests code reside). If your project structure doesn't follow the Gradle defined structure, then you can define that as my source code is not under src/main/java but is under src/java. The use of "sourceSet" section in this global file is only helping to use some conventional values in this global level file for ex: see integrationTest task and jacocoTestReport task below (you can't use those values if sourceSet is NOT defined in this file and if your project doesn't following the Gradle defined structure).
//NOTE2: Here in the global level Gradle file, I'm using values for sources for main, test, integrationTest etc as "dont_change_me" as I don't know what all projects (which will use this Gradle's global level file), will have what source code structure. The main / actual values of the sources for main, test and integrationTest task MUST be defined in the PROJECT's build.gradle file in sourceSets { main { java { srcDir 'src/java' } } } way.
sourceSets {
main {
java {
srcDir 'dont_change_me'
}
resources {
srcDir 'dont_change_me'
}
}
test {
java {
srcDir 'dont_change_me'
}
resources {
srcDir 'dont_change_me'
}
}
integrationTest {
java {
srcDir 'dont_change_me'
}
resources {
srcDir 'dont_change_me'
}
}
acceptanceTest {
java {
srcDir 'dont_change_me'
}
resources {
srcDir 'dont_change_me'
}
}
}
//...more code here
//...more code here
// The following is necessary to get code coverage info out. Compile with debug mode.
tasks.withType(Compile) {
options.debug = true
options.compilerArgs = ["-g"]
}
jacoco {
//toolVersion = "0.6.2.201302030002"
//toolVersion = "0.7.0.201403182114"
//toolVersion = "0.7.1.201404171759"
//This is latest than above, you may find later versions in online Maven repository.
toolVersion = "0.7.2.201409121644"
//OK I don't need the following folder to be created as I'll define my own.
// reportsDir = file("$buildDir/customJacocoReportDir")
}
//The following section is for UNIT tests (as build task in Gradle calls test task for free)
test {
maxParallelForks = 5
forkEvery = 50
ignoreFailures = true
// I want my reports (html) files to be created in a user defined folder UT(Unit test in build/reports/UT folder) and xml files (in user defined folder UT folder) under build/test-results/UT folder.
testReportDir = file("$buildDir/reports/tests/UT")
testResultsDir = file("$buildDir/test-results/UT")
//Following jacoco session will RUN in GRADLE's JVM session (during build / test time). This is different JVM than what many think of a runtime/Tomcat JVM session where we run a .war/.ear/etc file of an app to run that app and if you want to get code coverage of your main source code using non-unit tests from a Tomcat JVM, then see next task (integrationTest) as the following jacoco section in this "test" task is just for UNIT tests running in Gradle JVM session on a machine.
jacoco {
//NOTE: The following vars works ONLY with Gradle <= 1.6 version
// Create jacoco .exec file for Unit test in a user defined location
destPath = file("$buildDir/jacoco/UT/jacocoUT.exec")
//The following line is not that usesful acc. to my experience so commented it.
//classDumpPath = file("$buildDir/jacoco/UT/classpathdumps")
//NOTE: Following vars works only with versions >= 1.7 version of Gradle
//destinationFile = file("$buildDir/jacoco/UT/jacocoUT.exec")
// classDumpFile = file("$buildDir/jacoco/UT/classpathdumps")
}
}
task integrationTest( type: Test) {
//Always run tests
outputs.upToDateWhen { false }
//Ignore the failures if any during tests and don't mark the Gradle task as failed.
//You can comment this line if you want your gradle task to fail as soon as it finds any failing tests.
ignoreFailures = true
//This is telling Gradle that where it'll find class files from integration tests source code
testClassesDir = sourceSets.integrationTest.output.classesDir
//What path to use in classpath for integration tests
classpath = sourceSets.integrationTest.runtimeClasspath
//My custom location where I want my html reports files and xml result times of integration tests
testReportDir = file("$buildDir/reports/tests/IT")
testResultsDir = file("$buildDir/test-results/IT")
//Jacoco section in IT tests is NOT required here. Why as it'll never generage a coverage report this way as this way of using jacoco section in integrationTest task is telling Gradle to use jacoco in Gradle JVM and for getting code coverage you have to run jacoco/jacocoagent.jar in Target JVM (which is Tomcat or similar) by introducing jacocoagent.jar and other parameters for jacoco to Tomcat using one of Tomcat's -Dxxx option (see Jacoco help on how to do this). As the following is not required, I'm commenting the following jacoco code(otherwise if used, it'll always give you 0% coverage).
//jacoco {
//This works with 1.6
// destPath = file("$buildDir/jacoco/IT/jacocoIT.exec")
// classDumpPath = file("$buildDir/jacoco/IT/classpathdumps")
//Following works only with versions >= 1.7 version of Gradle
//destinationFile = file("$buildDir/jacoco/IT/jacocoIT.exec")
// classDumpFile = file("$buildDir/jacoco/IT/classpathdumps")
//}
}
jacocoTestReport {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
ignoreFailures = true
//Use any .exec file found before generating coverage report i.e. it'll give you combined coverage report if you have both jacocoUT.exec and jacocoIT.exec or other .exec files in build/jacoco/xx folders.
executionData = fileTree(dir: 'build/jacoco', include: '**/*.exec')
//executionData = files('build/jacoco/UT/jacocoUT.exec', 'build/jacoco/IT/jacocoIT.exec')
//executionData = files(['build/jacoco/UT/jacocoUT.exec', 'build/jacoco/IT/jacocoIT.exec'])
reports {
xml{
enabled true
//Following value is a file
destination "${buildDir}/reports/jacoco/xml/jacoco.xml"
}
csv.enabled false
html{
enabled true
//Following value is a folder
destination "${buildDir}/reports/jacoco/html"
}
}
//The following is an example of using Gradle conventional way of saying where is my main source code directory
//sourceDirectories = files(sourceSets.main.allJava.srcDirs)
sourceDirectories = files(['src/java', 'src/groovy'])
classDirectories = files('build/classes/main')
}
}
In Project's build.gradle, you define sourceSets section (with actual values) if your project structure if different than what Gradle says it should be. You define dependencies (i.e. for compile you need this .jar, for test or integrationTest, you need this .jar or .class from main/test etc).
Then, if you run gradle clean build, you'll get jacoco code coverage data for UT(Unit tests) under build/jacoco/UT folder in the form of an .exec file. Under build/reports/... html folder, you'll find jacoco main index.html which will show you the code coverage report.
If you want code coverage report from non-Unit tests (i.e. Integration tests etc), then attach the following parameter to the target JVM. I use Tomcat, so I'm attaching this, where testType is a variable which I use to find/tell whether I'm running IT(Integration Tests), AT (Acceptance Tests) or ST (Selenium GUI tests).
export PROJ_EXTRA_JVM_OPTS="-javaagent:tomcat/jacocoagent.jar=destfile=build/jacoco/${testType}/jacoco${testType}.exec,append=false"
and in startTomcat.sh script which I use, you'll notice I'm using the above variable and passing it to Tomcat's JVM as that's the JVM which is running my project's main .war/.ear file which has the main source code class files against which I want my code covreage data using Integration/Acceptance/other non-UNIT type tests:
## Tomcat command - JDK 1.6/Tomcat 6.0
TOMCAT_CMD="$JAVA_HOME/bin/java $TOMCAT_JVM_ARGS \
$OPTIT_JVM_ARGS \
$JPROF_JVM_ARGS \
$PROJ_EXTRA_JVM_OPTS \
org.apache.catalina.startup.Bootstrap $TOMCAT_CFG_FILE_ARGS start"
The above variable needs to be there in the Tomcat start script i.e. when Tomcat starts, it should get the above variable in its JVM session. Once this is done, you have to run "gradle integrationTest" then stop your Tomcat session(only then it'll flush the code coverage data to jacocoIT.exec file) and then if you run "gradle jacocoTestReport", it'll read the jacocoIT.exec file and genreate jacoco code coverage report for your main source code which your Integration/Acceptance/Selenium Tests tried to cover.

integrate ant junit generated test-results in gradle html report

In my gradle build script, I added doLast method to test task to run ant.junit task to execute tests from few jar files.
test << {
//run ant junit task with reports stored in $buildDir/test-results
//after ant junit completion and "test" task completion,
//how can I get the gradle generated html report include the above test-results?
}
How do I enhance this task to get the gradle html report benefit? I see that ant junit test xml reports are properly getting created in $buildDir/test-results along with other "gradle test" created xmls. However $buildDir/reports/tests" contains only. I was hoping that gradle will pick up the ant junit created test result xml files as well and include in its html report. But this is not happening. How can I get this behaviour?
I tried to create another task of type TestReport. But it also did not help.
task runTestsFromJar( type: TestReport ) {
destinationDir=file("$buildDir/reports/tests")
reportOn tasks.test, files("$buildDir/test-results/binary/test")
}
I am working with gradle 1.8.
I would suggest creating another task of type Test to replace your doAfter closure.
task antTests(type: Test){
//configuration here
}
I believe at that point you can use the TestReport task in a similar manner as your previous attempt
task runTestsFromJar( type: TestReport ) {
destinationDir=file("$buildDir/reports/tests")
reportOn test, antTests
}
My attempts to generate a report based on a directory of existing xml result files, and on the binary subfolder were unsuccessful as well
Based on the response from gradle forum post, it seems generate gradle styled test html report is not available out-of-the box with gradle. Gradle TestReport task seems to be dependent on the binary output files generated by "test" task. These are not generated when the tests are run using ant.JUnitTask from gradle.
I resorted to "ant JUnitReport task" finally to generate at least a meaningful consolidated report.
test << {
//run ant junit task with reports stored in $buildDir/test-results
//after ant junit completion
ant.junitReport( toDir: "$buildDir/reports") {
fileset ( dir:"$buildDir/test-results" )
report ( format:"frames", todir:"$buildDir/reports" )
}
}
This gives a basic html report. One can customize as needed using XSLT.

Resources