'Gradle problems of adding koin test dependencies

I'm a beginner with gradle and would like to use koin in my Kotlin project.

However, I get the following error

Execution failed for task ':compileTestKotlin'.
> Error while evaluating property 'filteredArgumentsMap' of task ':compileTestKotlin'
   > Could not resolve all files for configuration ':testCompileClasspath'.
      > Could not resolve org.jetbrains.kotlin:kotlin-test-junit5:1.6.20.
        Required by:
            project : > org.jetbrains.kotlin:kotlin-test:1.6.20
         > Module 'org.jetbrains.kotlin:kotlin-test-junit5' has been rejected:
              Cannot select module with conflict on capability 'org.jetbrains.kotlin:kotlin-test-framework-impl:1.6.20' also provided by [org.jetbrains.kotlin:kotlin-test-junit:1.6.10(junitApi)]
      > Could not resolve org.jetbrains.kotlin:kotlin-test-junit:1.6.10.
        Required by:
            project : > io.insert-koin:koin-test:3.2.0-beta-1 > io.insert-koin:koin-test-jvm:3.2.0-beta-1
         > Module 'org.jetbrains.kotlin:kotlin-test-junit' has been rejected:
              Cannot select module with conflict on capability 'org.jetbrains.kotlin:kotlin-test-framework-impl:1.6.10' also provided by [org.jetbrains.kotlin:kotlin-test-junit5:1.6.20(junit5Api)]```

This is my gradle.build.kts file

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

val koinVersion = "3.2.0-beta-1"

plugins {
    kotlin("jvm") version "1.6.20"
    kotlin("plugin.serialization") version "1.6.10"
    application
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
    implementation("io.insert-koin:koin-core:$koinVersion")
    testImplementation("io.insert-koin:koin-test:$koinVersion")
    testImplementation("io.insert-koin:koin-test-junit5:$koinVersion")
    testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.25")
    testImplementation(kotlin("test"))
}

tasks.test {
    useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "1.8"
}

application {
    mainClass.set("MainKt")
}


Solution 1:[1]

It looks like there are 3 problems

As I mentioned in the comment, the Kotlin JVM and Serialization plugins have mismatched versions. These should always be the same!

plugins {
  kotlin("jvm") version "1.6.21"
  kotlin("plugin.serialization") version "1.6.21"
  application
}

However, as you discovered, it still doesn't work. There's a larger error message, with three errors.

  1. Could not resolve io.insert-koin:koin-test-junit5:3.2.0-beta-1
  2. Could not resolve org.jetbrains.kotlin:kotlin-test-junit5:1.6.21
  3. Could not resolve org.jetbrains.kotlin:kotlin-test-junit:1.6.10

Let's go through them one-by-one

Java 11 library, Java 8 project

Here's the reason that Gradle gives for the first failure:

Could not resolve io.insert-koin:koin-test-junit5:3.2.0-beta-1.

No matching variant of io.insert-koin:koin-test-junit5:3.2.0-beta-1 was found. The consumer was configured to find an API of a library compatible with Java 8, preferably in the form of class files, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm' but

  • Incompatible because this component declares a component compatible with Java 11 and the consumer needed a component compatible with Java 8

The component, koin-test-junit5, is only compatible with Java 11, but your project needs Java 8 (kotlinOptions.jvmTarget = "1.8").

Let's fix this first, using Gradle Toolchain

tasks.withType<KotlinCompile>().configureEach {
  kotlinOptions.jvmTarget = "11"
}

kotlin {
  jvmToolchain {
    (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(11))
  }
}

That resolves the Java version mis-match, and leaves two more errors.

conflict on capability - incompatible libraries

Cannot select module with conflict on capability

Cannot select module with conflict on capability 'org.jetbrains.kotlin:kotlin-test-framework-impl:1.6.10' also provided by [org.jetbrains.kotlin:kotlin-test-junit:1.6.10(junitApi)]

Cannot select module with conflict on capability 'org.jetbrains.kotlin:kotlin-test-framework-impl:1.6.10' also provided by [org.jetbrains.kotlin:kotlin-test-junit5:1.6.10(junit5Api)]

Understanding this one requires quite a bit of knowledge of how Gradle selects versions.

tl;dr: org.jetbrains.kotlin:kotlin-test-junit and org.jetbrains.kotlin:kotlin-test-junit5 are incompatible. You can only use one or the other - not both

I don't really understand what Koin needs to work best. It looks like it has a hard dependency on JUnit5, so you'd have to use these dependencies, and wouldn't be able to use kotlin("test")

dependencies {
  implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
  implementation("io.insert-koin:koin-core:$koinVersion")
  testImplementation("io.insert-koin:koin-test:$koinVersion")
  testImplementation("io.insert-koin:koin-test-junit5:$koinVersion")
  testImplementation("com.willowtreeapps.assertk:assertk:0.25")
  testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")


// incompatible with JUnit 5, which I think is required by Koin?
// testImplementation(kotlin("test")) 
}

Explanation

In short, when you use Gradle to build a library, you can declare 'attributes'. They're free-form strings, so they can really be anything. They describe things like "this library needs Java 11" or "this is test coverage data".

Some attributes are important to Gradle resolving a project's dependencies. The error you originally got was caused by one such attribute: 'capability'. It describes the Maven coordinates that the library produces.

In the case of the Maven coordinates, if they clash, then Gradle doesn't know what to do, and throws an error. It's up to the user to fix it. There's a lot of Gradle docs about conflict resolution, but usually the simplest answer is to is remove any conflicting dependencies.

What's interesting about capabilities is that because it's just a string, you can add anything to it. And what the authors of org.jetbrains.kotlin:kotlin-test-junit5 and org.jetbrains.kotlin:kotlin-test-junit have done is given them both the same capability.

org.jetbrains.kotlin:kotlin-test-framework-impl:1.6.10

If you search for this library you'll find it doesn't exist. That's because the capability is completely artificial! The authors have made it up, specifically so Gradle will throw an error, and it's up to the user to fix it.

So that's the fix: choose either kotlin-test-junit or kotlin-test-junit5, because you can't have both.

Solution 2:[2]

I think org.jetbrains.kotlin:kotlin-test-junit5 has problem with dependencies.

I also struggled with the same issue, so I tried multiple solutions but it all fails.

And I realized that, when just add dependency for kotlin-test-junit5, the kotlin-test-junit is also added to the external libraries.

So here's the working solution for me.

I added this line to gradle first to enable useJunitPlatform()

tasks.withType<Test> {
    useJUnitPlatform()
}

after that, i exclude kotlin-test-junit from every references like this,

    testImplementation("io.ktor:ktor-server-tests-jvm:$ktorVersion")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion") {
        exclude(group = "org.jetbrains.kotlin", module = "kotlin-test-junit")
    }

    // Dependency Injection
    val koinVersion: String by project
    implementation("io.insert-koin", "koin-ktor", koinVersion)
    implementation("io.insert-koin", "koin-logger-slf4j", koinVersion)
    testImplementation("io.insert-koin", "koin-test-junit5", koinVersion) {
        exclude(group = "org.jetbrains.kotlin", module = "kotlin-test-junit")
    }

After that, junit5 test was working perfectly.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 aSemy
Solution 2 Hyukjoong Kim