0

I have a Compose multiplatform project that looks like this enter image description here

I have a set of tests in desktopTest, as intended. But I'd also like to have a set of tests that I run only when desired, when preparing for a release for example.

For this, I am trying to create an additional module (called desktopFullChainTest here).

I am struggling to find the build.gradle.kts configuration that will mirror the behavior of desktopTest (contains own set of dependencies, also includes desktopMain sources, includes desktopMain dependencies too).

I have a minimal reproduceable example here.

To reproduce :

  • Run ./gradlew desktopTest # All tests run successfully
  • Run ./gradlew desktopChainTest Test fails with a NoClassDefFoundError

What I've done :

I have originally modified the gradle file as such, to follow the documentation :

import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.composeMultiplatform)
    alias(libs.plugins.composeCompiler)
}

kotlin {
    jvm("desktop")

    sourceSets {
        val desktopMain by getting
        val desktopTest by getting

        val desktopFullChainTest by creating {
            dependsOn(desktopTest)
        }

        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material)
            implementation(compose.ui)
            implementation(compose.components.resources)
            implementation(compose.components.uiToolingPreview)
            implementation(libs.androidx.lifecycle.viewmodel)
            implementation(libs.androidx.lifecycle.runtime.compose)

            implementation(libs.ktor.client.core)
            implementation(libs.ktor.client.java)
            implementation(libs.ktor.client.logging)
            implementation(libs.ktor.client.logging.logback)

            implementation(libs.kbsky.core)

        }

        desktopMain.dependencies {
            implementation(compose.desktop.currentOs)
            implementation(libs.kotlinx.coroutines.swing)
        }

        desktopTest.dependencies {
            implementation(kotlin("test"))
            implementation(libs.junit5)
        }

        desktopFullChainTest.dependencies {
            implementation(kotlin("test"))
            implementation(libs.junit5)
        }

        tasks.withType<Test> {
            useJUnitPlatform()
        }
    }
}


compose.desktop {
    application {
        mainClass = "nl.lengrand.MainKt"

        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "nl.lengrand"
            packageVersion = "1.0.0"
        }
    }
}

but neither my IDE nor gradle recognize the declared dependencies

enter image description here

Junie has offered to specifically mention source sets :

import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.composeMultiplatform)
    alias(libs.plugins.composeCompiler)
}

kotlin {
    jvm("desktop")

    sourceSets {
        val desktopMain by getting
        val desktopTest by getting

        val desktopFullChainTest by creating {
            kotlin.srcDir("src/desktopFullChainTest/kotlin")
            resources.srcDir("src/desktopFullChainTest/resources")
        }

        afterEvaluate {
            val desktopMainCompilation = kotlin.targets.getByName("desktop").compilations.getByName("main")
            val desktopFullChainTestCompilation = kotlin.targets.getByName("desktop").compilations.create("fullChainTest")
            desktopFullChainTestCompilation.defaultSourceSet.dependsOn(desktopFullChainTest)
            desktopFullChainTestCompilation.associateWith(desktopMainCompilation)
        }

...

        desktopFullChainTest.dependencies {
            implementation(kotlin("test"))
            implementation(libs.junit5)
        }

...
    }
}

afterEvaluate {
    tasks.register<Test>("desktopFullChainTest") {
        description = "Runs full chain tests"

        val fullChainTestCompilation = kotlin.targets.getByName("desktop").compilations.getByName("fullChainTest")
        testClassesDirs = fullChainTestCompilation.output.classesDirs
        classpath = fullChainTestCompilation.output.classesDirs + 
                    fullChainTestCompilation.compileDependencyFiles + 
                    fullChainTestCompilation.output.resourcesDir.let { if (it.exists()) files(it) else files() }

        useJUnitPlatform()
    }
}


....

This fixes the import issues indeed, as well as the main module code imports, but all transitive dependencies from main aren't included either :

java.lang.NoClassDefFoundError: kotlinx/serialization/json/JsonBuilder

    at work.socialhub.kbsky.internal.share._InternalUtility.<clinit>(_InternalUtility.kt:24)
    at work.socialhub.kbsky.internal.com.atproto._ServerResource.createSession(_ServerResource.kt:43)
    at nl.lengrand.posters.DefaultBlueskyAPI.createSession(DefaultBlueskyAPI.kt:22)

This is not really surprising, since we are statically declaring a lot of locations, but I also fail to see a clearer way to do things in the docs.

Could someone please hint me at a valid configuration?

1 Answer 1

0

After trying many things, I haven't found a proper way to get this additional module working. For now, I decided to go for Plan B, keep all my tests together and use Junit's EnabledIfSystemProperty annotation instead.

So, my full chain tests look like this :

@EnabledIfSystemProperty(named = "test.release", matches = "true")
class GreetingTest {

    @Test
    fun connect(){
        assertThrows<ATProtocolException> { Greeting().connect() }

    }
}

which means that they'll be ignored all the time except if the specific system property is set.

In the command line, I then run

./gradlew :composeApp:desktopTest -Ptest.release=true

to run all tests.

To propagate that gradle run property to the Junit tests, I have to modify the build.gradle.kts file slightly :


tasks.withType<Test> {
    useJUnitPlatform()
    // If true, will run the full chain test and actually post content
    systemProperty("test.release", project.properties["test.release"]?:false)
}

Works way enough for now, as my project is small. Might update if I find a proper solution later.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.