I have a very basic gradle project, and have a test file with main(). The file - "src/test/javaTestRunner.java" is as follows:
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
public class TestRunner {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(TestJunit.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
The code compiles ("./gradlew classes" and "./gradlew testclasses" works fine). However, I am not able to execute TestRunner, not able to find the correct way to invoke the above class. I have tried the following:
./gradlew test --tests TestRunner
./gradlew test TestRunner
./gradlew TestRunner
"build.gradle" contains the following:
apply plugin: 'java'
apply plugin: 'application'
repositories {
jcenter()
}
dependencies {
compile 'com.google.guava:guava:21.0'
testCompile 'junit:junit:4.12'
}
// Define the main class for the application
mainClassName = 'MyFirstJavaProgram'
Any suggestions.
Thank you,
Ahmed.
A alternate way of running the main method as a gradle test:
Change the main method to a non-static method with no parameters and annotated with #Test:
public class TestRunner {
#Test
public void runTests() {
Result result = JUnitCore.runClasses(TestJunit.class);
for (Failure failure : result.getFailures()) { // consider using assertAll (JUnit5)
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful()); // consider using assertTrue (JUnit5)
}
}
Then ./gradlew test --tests TestRunner should now work.
Nice JUnit 5 tutorial Writing Tests with JUnit 5 by Trisha Gee of JetBrains.
Related
I have a project with the following files:
src/main/scala/package/Class.scala
src/test/java/package/ClassTest.java
When I run ./gradlew clean build test I get an error in :compileTestJava - it says "error: cannot find symbol" because apparently ClassTest.java cannot find Class.scala
I have the following in build.gradle which I think should work but doesn't:
sourceSets {
test {
java { srcDirs += ['src/main/scala', 'src/test/java'] }
}
}
I also have the following plugins for what it is worth:
plugins {
id 'java'
id 'scala'
id 'java-library'
id 'maven-publish'
id 'jacoco'
}
The only other things in build.gradle are repositories and dependencies
I have simulated your scenario locally. The below works with Gradle 6.5 and 'java' and 'scala' plugins. My main srcDirs are slightly different from yours but I do not think that is material.
build.gradle:
sourceSets
{
main
{
scala {
srcDirs = ['app/glue', 'app/controllers', 'app/models']
}
}
test {
java {
srcDirs = ['test/java']
}
scala {
srcDirs = ['test/scala']
}
}
}
Sample JUnit test which refers to a scala case class Form which is defined in the 'main' source set:
public class TestScalaFromJava
{
#Test
public void testNothing() { }
#Test
public void testScalaClassInJava1() {
Firm firm = new Firm(1, "hello", "mello", "yellow");
}
}
I can then run either of the below and they work:
./gradlew compileTestJava
./gradlew clean build test
I'm testing an app with JUnit5 and using Jacoco for coverage report.
Tests are executed Ok and test reports are present.
However, Jacoco report has the following logs if service contains methods, annotated with #Transactional
[ant:jacocoReport] Classes in bundle 'my-service' do no match with execution data. For report generation the same class files must be used as at runtime.
[ant:jacocoReport] Execution data for class mypackage/SampleService does not match.
This error occurres for all #Service classes methods, annotated with #Transactional, plain classes coverage is calculated ok.
Here's a sample test:
#SpringBootTest
#ExtendWith(SpringExtension.class)
public class MyServiceTest {
#Autowired
private SampleService sampleService;
#Test
public void doWork(){
sampleService.doWork();
}
}
Works fine. Coverage is non-zero:
public class SampleService {
public void doWork(){
System.out.println("HEY");
}
}
0% coverage:
public class SampleService {
#Transactional
public void doWork(){
System.out.println("HEY");
}
}
Transactional creates a proxy around actuall class. But, isn't there an out-of-box way for Jacoco to handle such a common situation?
I've tried #EnableAspectJAutoProxy annotaion with different flag variations, checked that up-to-date Jupiter engine and Jacoco plugin are used
Here's gradle config:
subprojects {
test {
useJUnitPlatform()
}
jacocoTestReport {
afterEvaluate {
classDirectories.from = files(classDirectories.files.collect {
fileTree(dir: it, exclude: '*Test.java')
})
}
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
}
}
}
Any help appreciated
I tried it with a test project, similar to what you've described, however I couldn't reproduce the issue. The only difference that I see between your project and mine, is that I've used maven instead of gradle.
Here is the test project:
https://github.com/gybandi/jacocotest
And here is the jacoco result for it (using org.springframework.transaction.annotation.Transactional annotation):
If this doesn't help you, could you upload your test project to github or some other place?
Edit:
#MikaelF posted a link to another answer, which shows how to add offline instrumentation for jacoco.
The solution that was described there worked for me, after I added the following block to build.gradle:
task instrument(dependsOn: [classes, project.configurations.jacocoAnt]) {
inputs.files classes.outputs.files
File outputDir = new File(project.buildDir, 'instrumentedClasses')
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
if (file(sourceSets.main.java.outputDir).exists()) {
def instrumentedClassedDir = "${outputDir}/${sourceSets.main.java}"
ant.'jacoco:instrument'(destdir: instrumentedClassedDir) {
fileset(dir: sourceSets.main.java.outputDir, includes: '**/*.class')
}
//Replace the classes dir in the test classpath with the instrumented one
sourceSets.test.runtimeClasspath -= files(sourceSets.main.java.outputDir)
sourceSets.test.runtimeClasspath += files(instrumentedClassedDir)
instrumented = true
}
if (instrumented) {
//Disable class verification based on https://github.com/jayway/powermock/issues/375
test.jvmArgs += '-noverify'
}
}
}
test.dependsOn instrument
There seems to be an open ticket on the jacoco plugin's github about this as well:
https://github.com/gradle/gradle/issues/2429
Based on single module instrumentation example https://stackoverflow.com/a/31916686/7096763 (and updated version for gradle 5+ by #MikaelF) here's example for multimodule instrumentation:
subprojects { subproject ->
subproject.ext.jacocoOfflineSourceSets = [ 'main' ]
task doJacocoOfflineInstrumentation(dependsOn: [ classes, subproject.configurations.jacocoAnt ]) {
inputs.files classes.outputs.files
File outputDir = new File(subproject.buildDir, 'instrumentedClasses')
outputs.dir outputDir
doFirst {
project.delete(outputDir)
ant.taskdef(
resource: 'org/jacoco/ant/antlib.xml',
classpath: subproject.configurations.jacocoAnt.asPath,
uri: 'jacoco'
)
def instrumented = false
jacocoOfflineSourceSets.each { sourceSetName ->
if (file(sourceSets[sourceSetName].output.classesDirs[0]).exists()) {
def instrumentedClassedDir = "${outputDir}/${sourceSetName}"
ant.'jacoco:instrument'(destdir: instrumentedClassedDir) {
fileset(dir: sourceSets[sourceSetName].output.classesDirs[0], includes: '**/*.class')
}
//Replace the classes dir in the test classpath with the instrumented one
sourceSets.test.runtimeClasspath -= files(sourceSets[sourceSetName].output.classesDirs[0])
sourceSets.test.runtimeClasspath += files(instrumentedClassedDir)
instrumented = true
}
}
if (instrumented) {
//Disable class verification based on https://github.com/jayway/powermock/issues/375
test.jvmArgs += '-noverify'
}
}
}
test.dependsOn doJacocoOfflineInstrumentation
}
full example here:
https://github.com/lizardeye/jacocomultimodulesample
Still, I think this is a durty hack, which can be easily broken with gradle or jacoco updates
My gradle pitest is not able to give me the right results. It looks like it is not able to locate my test files.
I have the following build.gradle file:
apply plugin: "java" apply plugin: "maven" apply plugin: "info.solidsoft.pitest"
group = "myorg" version = 1.0
repositories {
mavenCentral() }
sourceSets.all { set ->
def jarTask = task("${set.name}Jar", type: Jar) {
baseName = baseName + "-$set.name"
from set.output
}
artifacts {
archives jarTask
} }
sourceSets {
api
impl main{ java { srcDir 'src/api/java' srcDir 'src/impl/java' } } test { java { srcDir 'src/test/java' } } }
buildscript {
repositories {
mavenCentral()
//Needed only for SNAPSHOT versions
//maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
}
dependencies {
classpath 'info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.1.6'
} }
dependencies {
apiCompile 'commons-codec:commons-codec:1.5'
implCompile sourceSets.api.output
implCompile 'commons-lang:commons-lang:2.6'
testCompile 'junit:junit:4.9'
testCompile sourceSets.api.output
testCompile sourceSets.impl.output
runtime configurations.apiRuntime
runtime configurations.implRuntime }
jar {
from sourceSets.api.output
from sourceSets.impl.output }
pitest { println sourceSets.main
targetClasses = ['doubler.*'] targetTests = ['doubler.*'] verbose="on" }
THe output is stored in the correct folder. And when I run gradle test, it also runs fine.
Some additional information about this issue was supplied in the pitest user group.
https://groups.google.com/forum/#!topic/pitusers/8C7BHh-Vb6Y
The tests being run look like this.
#Test
public void testIt2() {
assert new DoublerImpl().testIt(1) == 2;
}
Pitest is correctly reporting that these tests provide 0% coverage of the class. There is no coverage because the assert keyword has been used.
Unless the -ea flag is set in the JVM running the tests assertions are disabled. There is basically hidden if block around this code generated by the compiler
#Test
public void testIt2() {
if (assertionsEnabled) {
assert new DoublerImpl().testIt(1) == 2;
}
}
As assertions are not enabled no code is executed.
To fix the issue use the built in JUnit assertions instead.
http://junit.sourceforge.net/javadoc/org/junit/Assert.html
This is my test class:
public class RoutingResponseRegressionOneByOne {
#BeforeClass
public static void oneTimeSetUp() {
routingClient = injector.getInstance(IRoutingClient.class);
System.out.println("in RoutingResponseRegressionOneByOne:: oneTimeSetUp");
}
I added a task to my build.gradle
task testRegression(type: Test) {
systemProperties['test.single'] = '\'RoutingResponseRegressionOneByOne\''
//systemProperty 'test.single', System.getProperty('test.single', '\'RoutingResponseRegressionOneByOne\'')
}
When I run "test" from the intellij this is the output
however when I "testRegression" from the intellij this is the output:
what should I fix in order for "testRegression" to run only this test class ?
You can pass an argument to your task with P and use the TestFilter.
In this link you will find a complete example for the filter usage. Below I attached a code of a custom automationTest task that runs tests from a specific folder.
task automationTest(type: Test) {
//search for an argument called 'single'. if exists, use it as a test filter
if (project.hasProperty('single')) {
filter {
includeTestsMatching "*${project.property('single')}*"
}
}
useTestNG()
description = "Runs Automation Tests"
testClassesDir = file("$buildDir/classes/main/automation")
classpath += sourceSets.main.runtimeClasspath
}
run it as: gradle -Psingle=MyTest automationTest
I have a gradle build script which currently works by simply executing a Java class through it's main method. What I want to know is, how can I call a static method in the same class but not have to go through the main method. The current gradle code is as follows:
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'java'
defaultTasks 'runSimple'
project.ext.set("artifactId", "test-java")
File rootDir = project.getProjectDir()
File targetDir = file("${rootDir}/target")
FileCollection javaClasspath = files("${targetDir}/tools.jar")
task(runSimple, dependsOn: 'classes', type: JavaExec) {
main = 'com.test.model.JavaTest'
classpath = javaClasspath
args 'arg1'
args 'arg2'
}
And my Java class as follows:
package com.test.model;
public class JavaTest {
public static void main(String[] args) throws Exception {
System.out.println("In main");
anotherMethod(args[0], args[1]);
}
public static void anotherMethod(String arg1, String arg2) {
System.out.println("In anotherMethod");
System.out.println(arg1 + " " + arg2);
}
}
This gives me the output:
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:runSimple
In main
In anotherMethod
arg1 arg2
BUILD SUCCESSFUL
Total time: 2.344 secs
My question is simply how can I skip the main method, and call the method "anotherMethod" directly from the gradle script? The output would then simply be:
In anotherMethod
arg1 arg2
Thanks
you have to add the jar or class to the classpath. here is an example with a jar file who contains the class.
Inside the file build.gradle add the dependencies.
My jar file is in the lib folder the path is lib/MQMonitor.jar.
import mypackage.MyClass
buildscript {
repositories {
flatDir name: 'localRepository', dirs: 'lib'
}
dependencies {
classpath name: 'MQMonitor'
}
}
task myTaskCallJava << {
MyClass.foo()
}
Assuming the class is on the buildscript classpath (it should be, since you're calling main from the same class)
task runSimple {
doLast {
com.test.model.JavaTest.anotherMethod("foo", "bar")
}
}
Tested on Gradle 4.6
I have been working on this too. You know I like the function of eclipse and intellij with the run with Junit options, and I want to do this using command line and gradle.
If you could accept putting your test method in the directory of 'test' directory of gradle. I actually have a fair solution.
package com.udacity.gradle;
import org.junit.Test;
public class TestClass {
#Test
public void anotherMethod() {
System.out.println("This is it, I want this!!!");
}
#Test
public void notMyWantedMethod1() {
System.out.println("not my wanted");
}
public void notMyWantedMethod2() {
System.out.println("not my wanted");
}
}
This my test class which is in src/test/java/com/udacity/gradle/TestClass.java
Then the under below is the file of my build.gradle
apply plugin: "java"
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
test {
testLogging.showStandardStreams = true
filter {
//include specific method in any of the tests
includeTestsMatching "*.TestClass.anotherMethod"
}
}
Simple idea you know this is a test class so I use the test task of gradle. And to specify which method to use, I added a Test filter which could specify down to method.
Then you could just run
gradle test
Then you could find in console that you have what you want in there. However, remember to add
testLogging.showStandardStreams = true
if you don't do this, gradle would swallow your console output. But even if you don't add this line. You could read a test log in the directory of
...../build/test-results/test/TEST-com.udacity.gradle.TestClass.xml
There are well organized test reports output in it.
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="com.udacity.gradle.TestClass" tests="1" skipped="0" failures="0" errors="0" timestamp="2018-03-31T19:26:44" hostname="hexin-PC" time="0.022">
<properties/>
<testcase name="anotherMethod" classname="com.udacity.gradle.TestClass" time="0.022"/>
<system-out><![CDATA[This is it, I want this!!!
]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>
If you want to execute a static method you will need to add the class to the Gradle build script's classpath.
To add the code to the build scripts classpath if your code is in a repository:
buildscript {
repositories {
maven { url "${yourRepositoryURL}" }
}
dependencies {
classpath 'com.yourgroup:yourpackagename:version'
}
}
To add the code to the build scripts classpath if you code is built locally (I didn't test this one):
buildscript {
dependencies {
classpath files("path/to/where/the/class/files/are")
}
}
Then you should be able to call that method just like any other:
task runSimple(dependsOn: 'classes') {
doFirst() {
com.test.model.JavaTest.anotherMethod('arg1', 'arg2')
}
}