Error: “Cannot create Launcher without at least one TestEngine” when trying to execute test task on a Kotlin MMP in shared Module

  Kiến thức lập trình

I have an issue when trying to run my test tasks on my kotlin MMP. I get the following error:

org.gradle.api.internal.tasks.testing.TestSuiteExecutionException: Could not start Gradle Test Executor 5.
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.startProcessing(SuiteTestClassProcessor.java:44)
    at [email protected]/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at [email protected]/java.lang.reflect.Method.invoke(Method.java:577)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at jdk.proxy1/jdk.proxy1.$Proxy2.startProcessing(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker$1.run(TestWorker.java:161)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
    at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
    at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: org.junit.platform.commons.PreconditionViolationException: Cannot create Launcher without at least one TestEngine; consider adding an engine implementation JAR to the classpath
    at app//org.junit.platform.commons.util.Preconditions.condition(Preconditions.java:296)
    at app//org.junit.platform.launcher.core.DefaultLauncher.<init>(DefaultLauncher.java:55)
    at app//org.junit.platform.launcher.core.LauncherFactory.createDefaultLauncher(LauncherFactory.java:134)
    at app//org.junit.platform.launcher.core.LauncherFactory.openSession(LauncherFactory.java:98)
    at app//org.junit.platform.launcher.core.LauncherFactory.openSession(LauncherFactory.java:82)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$BackwardsCompatibleLauncherSession.open(JUnitPlatformTestClassProcessor.java:318)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.createTestExecutor(JUnitPlatformTestClassProcessor.java:81)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.startProcessing(AbstractJUnitTestClassProcessor.java:50)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.startProcessing(SuiteTestClassProcessor.java:42)
    ... 16 more

This is my gradle configuration of my shared module of the kotlin MMP:

plugins {
    alias(libs.plugins.kotlin.multiplatform)
    alias(libs.plugins.android.library)
    alias(libs.plugins.kotlin.serialization)
}

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
    targetHierarchy.default()

    android {
        compilations.all {
            kotlinOptions {
                jvmTarget = libs.versions.jvmTarget.get()
            }
        }
    }

    jvm {
        compilations {
            val test by compilations.getting {
                defaultSourceSet {
                    dependencies {
                        implementation(libs.kotest.framework.engine)
                        implementation(libs.kotest.kotest.assertions.core)
                        implementation(libs.mockk.common)
                        implementation(libs.mockk.jvm)
                        implementation("org.jetbrains.kotlin:kotlin-test")
                        implementation("io.kotest:kotest-runner-junit5:5.5.4")

                        implementation(kotlin("test"))
                        implementation(kotlin("test-common"))
                        implementation(kotlin("test-annotations-common"))
                        implementation(libs.junit.jupiter.api) // Ensure JUnit Jupiter API
                        runtimeOnly(libs.junit.jupiter.engine) // Add JUnit Jupiter Engine
                    }
                    kotlin.srcDir("src/conformanceTest/kotlin")
                    resources.srcDirs("src/conformanceTest/resources")
                    dependsOn(sourceSets.getByName("commonTest"))
                }
                tasks.register<Test>("conformanceTest") {
                    group = "verification"
                    description = "Runs the conformance tests."
                    testClassesDirs = output.classesDirs

                    useJUnitPlatform{
                        includeEngines("junit-jupiter")
                    }
                    maxHeapSize = "2048m"
                    jvmArgs("-XX:MaxMetaspaceSize=512m")
                    testLogging {
                        events("passed", "skipped", "failed")
                        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
                        showStandardStreams = true
                    }
                }

                tasks.register<Test>("kotestTest") {
                    group = "verification"
                    description = "Runs the conformance tests."
                    testClassesDirs = output.classesDirs

                    useJUnitPlatform{
                        includeEngines("kotest")
                    }
                    maxHeapSize = "2048m"
                    jvmArgs("-XX:MaxMetaspaceSize=512m")
                    testLogging {
                        events("passed", "skipped", "failed")
                        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
                        showStandardStreams = true
                    }
                }
            }
            dependencies{
                testImplementation(libs.junit.jupiter.api) // Ensure JUnit Jupiter API
                testRuntimeOnly(libs.junit.jupiter.engine) // Add JUnit Jupiter Engine
                testRuntimeOnly("org.junit.vintage:junit-vintage-engine")
            }
        }
        compilations.all{
            kotlinOptions {
                jvmTarget = libs.versions.jvmTarget.get()
            }
        }
    }



    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "shared"
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                //put your multiplatform dependencies here
                api(libs.kotlinx.datetime)
                implementation(libs.kotlinx.coroutines.core)
                api(libs.bundles.ktor)
                api(libs.kotlin.reflection)
                api(libs.kotlin.stdlib)
            }
        }
        val commonTest by getting {
            dependsOn(commonMain)
            dependencies {
                implementation(kotlin("test"))

                // reference: https://kotest.io/docs/quickstart/#assertions-library
                implementation(libs.kotest.kotest.assertions.core)

                // reference: https://kotest.io/docs/framework/project-setup.html
                implementation(libs.kotest.framework.engine)

                // https://mvnrepository.com/artifact/io.mockk/mockk-common
                implementation(libs.mockk.common)

                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            }
        }
        val androidMain by getting {
            dependsOn(commonMain)
            dependencies {
                implementation(libs.appauth)
                implementation(libs.androidx.espresso.core)
                implementation(libs.androidx.junit)
                implementation(libs.mockk.android)
                implementation(libs.ktor.client.android)
            }
        }

        val androidUnitTest by getting {
            dependsOn(androidMain)
            dependsOn(commonTest)
        }

        val androidInstrumentedTest by getting {
            dependsOn(androidMain)
            dependsOn(commonTest)
        }

        val jvmMain by getting {
            kotlin.srcDir("src/conformanceMain/kotlin")
            resources.srcDir("src/conformanceMain/resources")
            dependsOn(commonMain)
        }

        val jvmTest by getting {
            kotlin.srcDir("src/conformanceTest/kotlin")
            resources.srcDir("src/conformanceTest/resources")
            dependencies {
                implementation(libs.kotest.framework.engine)
                implementation(libs.kotest.kotest.assertions.core)
                implementation(libs.mockk.common)
                implementation(libs.mockk.jvm)
                implementation(kotlin("test"))
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
                val jvmTestImplementation by configurations.getting {
                    dependencies {
                        implementation(libs.junit.jupiter.api) // Ensure JUnit Jupiter API
                        implementation(libs.junit.jupiter.engine) // Add JUnit Jupiter Engine
                    }
                }
            }
            dependsOn(jvmMain)
            dependsOn(commonTest)
        }
    }
}

android {
    lint {
        // If set to true (default), stops the build if errors are found.
        abortOnError = false
        // If set to true, lint only reports errors.
        ignoreWarnings = false
        // If set to true, lint also checks all dependencies as part of its analysis.
        // Recommended for projects consisting of an app with library dependencies.
        checkDependencies = true
    }

    namespace = "com.docuware.cygnus"
    compileSdk = libs.versions.targetSdk.get().toInt()
    defaultConfig {
        minSdk = libs.versions.minSdk.get().toInt()
        manifestPlaceholders["appAuthRedirectScheme"] = "cygnus"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    // Throws 2 files found with path 'win32-x86-64/attach_hotspot_windows.dll' otherwise
    packaging {
        resources.excludes.add("**/attach_hotspot_windows.dll")
        resources.excludes.add("META-INF/**")
    }

    compileOptions {
        sourceCompatibility = JavaVersion.toVersion(libs.versions.jvmTarget.get())
        targetCompatibility = JavaVersion.toVersion(libs.versions.jvmTarget.get())
    }
}

I thought i have added all the dependencies correctly, but the execution of the task always complains that the test did fail due to above error message.
I really am having trouble overall with gradle, as i’m quite new to it and have to maintain our already existing gradle file while adding conformance tests to it, so my knowledge about how to do stuff like this is still limited. AI unfortunately is not much of a help here, as it suggests stuff, which the compiler complains about or which simply does not work. I also tried the stuff on stackoverflow i could find for this issue (at least the stuff that is not 6 years old).

So if anyone out there would be able to help me on this issue, i would be really grateful.
If you need any further information about the project structure, please let me know.

Best regards,
Maverick

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT