unbroken-dome / gradle-testsets-plugin

A plugin for the Gradle build system that allows specifying test sets (like integration or acceptance tests).
MIT License
230 stars 50 forks source link

Plugin does not work with gradle kotlin dsl - unresolved reference: integrationTestImplementation #109

Closed u6f6o closed 4 years ago

u6f6o commented 4 years ago

I added the gradle-testsets-plugin to our existing kotlin dsl build files and initially everything worked smoothly. After I moved the dependencies to integrationTestImplementation though, the build stopped working with the following exception:

gradle-testsets-playground ./gradlew integrationTest

> Configure project :
e: /Users/ugitschthaler/Engineering/projects/external/gradle-testsets-playground/build.gradle.kts:18:2: Unresolved reference: integrationTestImplementation

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/ugitschthaler/Engineering/projects/external/gradle-testsets-playground/build.gradle.kts' line: 18

* What went wrong:
Script compilation error:

  Line 18:      integrationTestImplementation("com.github.tomakehurst:wiremock-jre8:2.26.3")
            ^ Unresolved reference: integrationTestImplementation

1 error

* 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

When I remove the dependency, the build works again. Here is a little reproducer:

plugins {
   id("org.unbroken-dome.test-sets") version "3.0.1"
}

testSets {
    create("integrationTest")
}

tasks.withType<Test> {
    useJUnitPlatform()
}

tasks.named<Test>("integrationTest") {
    useJUnitPlatform()
}

dependencies {  
    integrationTestImplementation("com.github.tomakehurst:wiremock-jre8:2.26.3")
}

Could it be related to https://stackoverflow.com/questions/52904603/integration-tests-with-gradle-kotlin-dsl maybe?

u6f6o commented 4 years ago

If I use the same setup with groovy dsl it works though:

plugins {
    id 'org.unbroken-dome.test-sets' version '3.0.1'
}

testSets {
    integrationTest
}

dependencies {
    // Wiremock will only be available in integration tests, but not in unit tests
    integrationTestImplementation 'com.github.tomakehurst:wiremock:2.26.3'
}

tasks.withType(Test) {
    useJUnitPlatform()
}
sean-abbott commented 4 years ago

I was also hoping to use this, but can't figure out how. Is there a workaround to make this work?

rbolkey commented 4 years ago

@sean-abbott there is a workaround:

dependencies {

    // works
    add("integrationTestImplementation", "com.github.tomakehurst:wiremock:2.26.3")

    // also works
    "integrationTestImplementation"("com.github.tomakehurst:wiremock:2.26.3")
}

looks like you can refer to the configuration as a string and add dynamically, but the configuration isn't being added in the generated Kotlin DSL code.

sean-abbott commented 4 years ago

Awesome. Thank you. If it's not too much trouble, would you mind explaining to a kotlin-and-gradle noob what the mechanisms of working/not working are here, so I can start to form the ability to reason about this?

I totally understand if that's too much trouble, 'cause definitely not your problem. :-)

rbolkey commented 4 years ago

Hard to be succinct on the inner workings of Gradle here, but a couple starting points:

Definitely recommend reading through the Gradle User Manual and the DSL reference if you haven't and want a deeper understanding.

zyrain commented 4 years ago

Similarly, the API dependencies of testSets are also broken

sean-abbott commented 4 years ago

The workaround doesn't appear to work for me. I have, abbreviated:

allprojects {
    repositories {
        jcenter()
    mavenCentral()
    gradlePluginPortal()
  //snip
}

subprojects {
  apply(plugin = "org.unbroken-dome.test-sets")
  //snip
  dependencies {
    //snip
    implementation(platform("org.testcontainers:testcontainers-bom:1.14.3"))
    // works, but commented to try
    // testImplementation("org.testcontainers:mysql")
    // "integrationTestImplementation"("org.testcontainers:mysql")  // fails with Configuration with name 'integrationTestImplementation' not found
    // add("integrationTestImplementation", "org.testcontainers:msyql")  // same

  }
    testSets {
        val integrationTest by creating {}
    }
  }

(this is my top level build.gradle.kts). I understand my coordinates might not be correct for the workaround, BUT the complaints isn't the coordinates it's that it can't find the configuration.

tkrullmann commented 4 years ago

Sorry for being late to the discussion... this is a "limitation" of the Kotlin DSL, there's nothing I can do about it in the plugin.

dependencies {
    "integrationTestImplementation"("com.example:foo:1.2.3")
}

This is not only true inside the dependencies block but pretty much in any DSL container that supports named objects, e.g. tasks.

Now, what the Kotlin DSL does in addition if the java plugin is applied, is that they statically define some extension methods for well-known configuration names like implementation, testCompileOnly and so forth. That's why you can use these without quotes.

Any other configuration names which are dynamically determined by the build script must be enclosed in quotes. The only way around this would be if custom plugins had access to the Kotlin DSL stub generation (or whatever it's called). This stub consists of a just-in-time generated Kotlin class library tailored to the plugins used in their build script. To my knowledge, this is not something that Gradle has published an API for or allows third-party plugins to modify.

will-molloy commented 1 year ago

Has anyone got it working with a multi-project setup? Seems testSets can't be configured in subprojects block:

subprojects {
  // with declarative DSL (i.e. plugin id("org.unbroken-dome.test-sets") set above)
  testSets {
  }

  apply(plugin = "org.unbroken-dome.test-sets")
  configure<TestSetsPlugin> {
  }
}

Both methods get this error: Extension of type 'TestSetContainer' does not exist. Didn't have this problem when using groovy.

Edit: it works if I apply in each submodule build.gradle.kts (i.e. no subprojects or allprojects block required). But duplicate code :/