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