How to avoid "No tests were found" when using JUnit 5 with Groovy?
In this short blog post I would like to explain how to avoid popular mistake when you write your first JUnit 5 test case in Groovy.
The example
Let’s start with the example. Here is our build.gradle
file that adds JUnit 5 dependency:
plugins {
id 'groovy'
id 'idea'
}
repositories {
jcenter()
}
dependencies {
compile localGroovy()
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
}
test {
useJUnitPlatform()
}
And here is our test class:
import org.junit.jupiter.api.Test
class UnitTest {
@Test
def shouldThrowAnException() {
throw new RuntimeException()
}
}
This test does nothing, but it’s fine. We just want to execute this test.
The problem
The solution
The problem we shown above is caused by a single def
keyword we used in shouldThrowAnException
prototype. This is common mistake, made very often by people who have experience with Spock Framework (which tolerates def
btw).
So, what is the problem with def
keyword? Firstly, let’s take a look at the decompiled class:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.junit.jupiter.api.Test;
public class UnitTest implements GroovyObject {
public UnitTest() {
CallSite[] var1 = $getCallSiteArray();
super();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
@Test
public Object shouldThrowAnException() {
CallSite[] var1 = $getCallSiteArray();
throw (Throwable)var1[0].callConstructor(RuntimeException.class);
}
}
As you can see Groovy’s def
keyword gets compiled to Object
type in this case. OK, but what is wrong with that? Well, JUnit 5 method resolver uses IsTestMethod
predicate which requires that test method returns void
. Otherwise method annotated with @Test
does not get resolved.
If we only replace def
with void
keyword:
import org.junit.jupiter.api.Test
class UnitTest {
@Test
void shouldThrowAnException() {
throw new RuntimeException()
}
}
the test will execute and fail as expected:
% gradle test
> Task :test FAILED
UnitTest > shouldThrowAnException() FAILED
java.lang.RuntimeException at UnitTest.groovy:7
1 test completed, 1 failed
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///home/wololock/workspace/groovy-junit5/build/reports/tests/test/index.html
* Try:
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 1s
2 actionable tasks: 2 executed
0 Comments