Search code examples
javaspring-bootgradlejunit5junit-jupiter

Java, Spring Boot & JUnit 5 NoSuchMethod error on getExecutableInvoker


Java 17, Gradle 8.3 and Spring Boot 3.2.4 here. My build.gradle looks like:

plugins {
    id 'application'
    id "io.freefair.lombok" version '6.4.0'
    id 'org.springframework.boot' version '3.2.4'
    id 'io.spring.dependency-management' version '1.1.4'
}

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {

    implementation (

            'org.apache.commons:commons-lang3:3.12.0'
            ,'commons-io:commons-io:2.11.0'
            ,'org.projectlombok:lombok:1.18.16'

            ,"org.springframework.boot:spring-boot-starter"
            ,"org.springframework.boot:spring-boot-starter-actuator"
            ,"org.springframework.boot:spring-boot-starter-web"
            ,"org.springframework.boot:spring-boot-starter-data-jpa"
            ,"org.springframework.boot:spring-boot-starter-data-rest"
            ,"org.springframework.boot:spring-boot-starter-jdbc"
            ,"org.springframework.boot:spring-boot-starter-validation"
            ,"org.springframework.boot:spring-boot-starter-logging"
            ,"org.springframework.boot:spring-boot-starter-thymeleaf"
            ,"org.springframework.boot:spring-boot-starter-security"
            ,"org.springframework.boot:spring-boot-starter-oauth2-client"
            ,"org.springframework.boot:spring-boot-starter-mail"

            ,'mysql:mysql-connector-java:8.0.30'
            ,'org.liquibase:liquibase-core:4.26.0'

            ,'org.slf4j:log4j-over-slf4j:1.7.33'

    )

    testImplementation (
        'org.springframework.boot:spring-boot-starter-test'
        ,'org.springframework.security:spring-security-test'
        'org.junit.jupiter:junit-jupiter-api:5.8.1'
        ,'org.springframework.boot:spring-boot-starter-test'
        ,'org.springframework.security:spring-security-test'
        ,'com.h2database:h2:2.1.214'
        ,'org.mockito:mockito-core'
    )

    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'

}

tasks.named('test') {
    useJUnitPlatform()
}

bootJar {
    archiveFileName = "myapp.jar"
}

java {
    sourceCompatibility = '17'
}

I have a base unit test class:

@SpringBootTest
public abstract class BaseUnitTest {

    @BeforeEach
    public abstract void setupForEach();

    protected UserEntity createMockUser() {
        return Mockito.mock(UserEntity.class);
    }

    protected OrgEntity createMockOrganization() {
        return Mockito.mock(OrgEntity.class);
    }

    // other test helper methods

}

And a test class:

public class RegistrationServiceTest extends BaseUnitTest {

    @MockBean
    private UserService userService;

    @MockBean
    private OrgService orgService;

    @Autowired
    private RegistrationService registrationService;

    @Override
    public void setupForEach() {

    }

    @Test
    public void inviteUser_whenInputIsGood_thenUserIsCreatedAndInviteIsSent() {

        // setup
        UserEntity newUser = createMockUser();
        OrgEntity org = createMockOrganization();

        when(userService.getByEmail(eq("some email"), anyBoolean())).thenReturn(null);
        when(organizationService.getByRefId(anyString())).thenReturn(org);

        // test
        UserInvitationEntity result = registrationService.inviteUser(user);

        // check
        assertNotNull(result);

    }

}

When I run this test class I get the following exception:

java.lang.NoSuchMethodError: 'org.junit.jupiter.api.extension.ExecutableInvoker org.junit.jupiter.api.extension.ExtensionContext.getExecutableInvoker()'
    at org.springframework.test.context.junit.jupiter.SpringExtension.registerMethodInvoker(SpringExtension.java:381)
    at org.springframework.test.context.junit.jupiter.SpringExtension.beforeAll(SpringExtension.java:132)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllCallbacks$10(ClassBasedTestDescriptor.java:381)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:381)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:205)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:80)
    <large stacktrack omitted for brevity>
    Suppressed: java.lang.NoSuchMethodError: 'org.junit.jupiter.api.extension.ExecutableInvoker org.junit.jupiter.api.extension.ExtensionContext.getExecutableInvoker()'
        at org.springframework.test.context.junit.jupiter.SpringExtension.registerMethodInvoker(SpringExtension.java:381)
        at org.springframework.test.context.junit.jupiter.SpringExtension.afterAll(SpringExtension.java:143)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$16(ClassBasedTestDescriptor.java:447)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$17(ClassBasedTestDescriptor.java:447)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeAfterAllCallbacks(ClassBasedTestDescriptor.java:447)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:229)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:80)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:161)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:161)
        ... 48 more

Don't worry about the coding/implementation of my test class/method, I'm curious as to why I'm getting this java.lang.NoSuchMethodError. I suspect its incompatible versions of Java, Spring Boot and JUnit but I can't tell where I'm going wrong.

Update

After removing the explicitly-managed JUnit, Mockito and H2 dependencies in my build.gradle, I am still getting errors, but the error I am receiving has changed slightly:


java.lang.AbstractMethodError: Receiver class org.junit.jupiter.engine.descriptor.ClassExtensionContext does not define or inherit an implementation of the resolved method 'abstract org.junit.jupiter.api.extension.ExecutableInvoker getExecutableInvoker()' of interface org.junit.jupiter.api.extension.ExtensionContext.
    at org.springframework.test.context.junit.jupiter.SpringExtension.registerMethodInvoker(SpringExtension.java:381)
    at org.springframework.test.context.junit.jupiter.SpringExtension.beforeAll(SpringExtension.java:132)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllCallbacks$10(ClassBasedTestDescriptor.java:381)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:381)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:205)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:80)
<large stack trace omitted for brevity>
    Suppressed: java.lang.AbstractMethodError: Receiver class org.junit.jupiter.engine.descriptor.ClassExtensionContext does not define or inherit an implementation of the resolved method 'abstract org.junit.jupiter.api.extension.ExecutableInvoker getExecutableInvoker()' of interface org.junit.jupiter.api.extension.ExtensionContext.
        at org.springframework.test.context.junit.jupiter.SpringExtension.registerMethodInvoker(SpringExtension.java:381)
        at org.springframework.test.context.junit.jupiter.SpringExtension.afterAll(SpringExtension.java:143)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$16(ClassBasedTestDescriptor.java:447)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$17(ClassBasedTestDescriptor.java:447)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeAfterAllCallbacks(ClassBasedTestDescriptor.java:447)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:229)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:80)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:161)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:161)
        ... 48 more

Solution

  • The problem is that you are including explicit dependencies for junit-jupiter-engine and junit-jupiter-api. You are including version 5.8.1 whereas I think Spring Boot 3.2 requires 5.10.

    However you don't need those dependencies as those are already pulled in as part of the spring-boot-starter-test. Removing them will also prevent you in future updates getting incompatible versions.

    The solution remove the dependencies or at least the version number. But I would delete them all together.

    dependencies {
    
        implementation (
                'org.apache.commons:commons-lang3:3.12.0'
                ,'commons-io:commons-io:2.11.0'
                ,'org.projectlombok:lombok'
                ,"org.springframework.boot:spring-boot-starter-actuator"
                ,"org.springframework.boot:spring-boot-starter-web"
                ,"org.springframework.boot:spring-boot-starter-data-jpa"
                ,"org.springframework.boot:spring-boot-starter-data-rest"
                ,"org.springframework.boot:spring-boot-starter-jdbc"
                ,"org.springframework.boot:spring-boot-starter-validation"
                ,"org.springframework.boot:spring-boot-starter-thymeleaf"
                ,"org.springframework.boot:spring-boot-starter-security"
                ,"org.springframework.boot:spring-boot-starter-oauth2-client"
                ,"org.springframework.boot:spring-boot-starter-mail"
                ,'com.mysql:mysql-connector-j'
                ,'org.liquibase:liquibase-core'
                ,'org.slf4j:log4j-over-slf4j'
        )
    
        testImplementation (
            'org.springframework.boot:spring-boot-starter-test'
            ,'org.springframework.security:spring-security-test'
            ,'com.h2database:h2'
        )
    }
    

    TIP: Mockito is also already pulled in by spring-boot-starter-test and the H2 version is managed as well.

    TIP2: Lombok is also managed by Spring Boot, remove that version as well to get a compatible version. Or at least use the most recent one as older versions tent not to work well with newer Java versions. (Or ditch Lombok altogether but that is another discussion ;) ).

    TIP3: I changed the MySQL driver to the one managed by Spring Boot (and the newer coordinates).

    TIP4: Your SLF4J version 1.7.33 wasn't compatible either as Spring Boot uses 2.0.x, remove the version or remove the dependency altogether as this is already pulled in automatically.

    TIP5: You had duplicate dependencies in your testImplemementation (Removed) and in your general implementation you don't need the spring-boot-starter and spring-boot-starter-logging as those are pulled in by all other spring-boot-starter-* dependencies already.