Are there multiplatform test libraries that suppor...
# multiplatform
c
Are there multiplatform test libraries that support dynamically generating test cases/parametrized tests? As far as I know kotlin.test doesn't support them, and Kotest is currently broken on Kotlin/JS. Is there anything else I'm missing?
a
kotlin.test _sort of_has a pattern for parameterised tests, using local functions
Copy code
@Test
  fun myTest() {
    val myService = MyService()
    
    fun testService(expected: String, data1: Int, data2: Int) {
      val actual = myService.someOperation(data1, data2)
      assertEquals(expected, actual)
    }

    testService("blah 1 2", 1, 2)
    testService("blah 2 2", 2, 2)
    testService("blah 3 2", 3, 2)
    testService("blah 4 2", 4, 2)
  }
It’s not ideal because it doesn’t create separate tests in the reports, and the tests fail early, but it works well enough. It’s used a lot in Kotlinx Serialization tests.
c
the tests fail early
That's the main thing I dislike
a
do you know if it’s a specific part of Kotest that’s not compatible with Kotlin/JS? Because if it’s only the test runner, but the PBT generators still works, then you could use kotlin.test + Kotest PBT
c
For context, I have multiple implementations of the same
interface
that share their test cases (to ensure they all respect the interface's contract). What I'm currently doing is
Copy code
// Module test

abstract class FooTestCases<T : Foo> {
    abstract suspend fun new(): T

    @Test
    fun create() = runTest {
        val foo = new()

        // test…
    }
}

// For each impl…

class FooImplTest : FooTestCases<FooImpl> {
    override fun new() = FooImpl(…)
}
This approach worked well (tests do not fail early, there is a proper test suite per implementation with proper naming etc); however it fails spectacularly when some objects have dependencies because the abstract class and implementation both initialize different objects. I was considering Kotest because it would allow me to declare something like (have not used it yet so syntax may be wrong, but from the documentation this should be possible):
Copy code
fun TestCase.testFooContract(new: () -> Foo) {
    "create" - {
        val foo = new()

        // …
    }

    // …
}

class Impl1 : StringSpec({
    testFooContract { FooImpl1(…) }
})

class Impl2 : StringSpec({
    testFooContract { FooImpl2(…) }
})
which is much more ergonomic.
@Adam S the JS test runner doesn't report anything to Gradle or IntelliJ, so I can't see which tests pass or fail (except in the HTML report, but that's hell to play with) → https://github.com/kotest/kotest/issues/3400 You're saying it's possible to get the Kotest syntax while still using the kotlin.test runner? Do you think this would work in this case?
a
okay clear, and yes for dynamic test registration you’d need the Kotest runner. Although I think that Kotest still doesn’t support nested JS tests (the issue is stale https://github.com/kotest/kotest/issues/3141), so even if Kotest worked for you I suspect that would be a big hinderance
I don’t know if kotlin.test supports dynamic test registration. If it does, then you could create Kotest-like syntax. For example, dynamic test registration is possible with JUnit 5 https://gist.github.com/bastman/248c7022d699f2bcbfba25ca358e66aa
maybe this? https://discuss.kotlinlang.org/t/kotlin-test-js-specifying-test-without-annotation/11280
Copy code
@JsModule("kotlin-test")
external val kTest:dynamic

kTest.kotlin.test.suite("package test", false){
            kTest.kotlin.test.suite("suit test", false) {
                kTest.kotlin.test.test("test", false) {
                    assertTrue(true)
                }
            }
        }
c
This is JS-specific?
I support JVM+JS
a
you could expect/actual it
that post is 4 years old so it’s probably outdated
c
😕
a
c
Just found it, I'm writing a comment there 🙂
not very hopeful it's going to be moving anytime soon though
a
probably not - I expect that a fix for Kotest would come sooner
c
I'm not sure of that. I seems the Kotest team prefers to stay with old Kotlin versions. Kotest also lacks some other things I'd need (e.g. KotlinX.coroutines.test's
backgroundScope
) that would take even more time to get added.
a
dynamic tests on Kotlin/JS! this uses Kotlin 1.8.10, JS(IR) browser
image.png
c
Oh it still works!
I'll expect-actual that and the junit5 solution then, thanks!
a
it needed a bit of tweaking, but yes, it basically just worked
and because it doesn’t require the Kotest test runner, you could also use Kotest PBT
I’m not sure how it will work with
suspend
but I think that it’s fine
c
Kotest PBT?
a
Property Based Testing
c
I haven't used that before. I'll look into it in the future
a
Kotest is basically 3 libraries under one umbrella. The test runner (the dynamic test generation that you’re intested in), the assertions (which are very fluid and Kotlin-esque), and Property Based Testing. They can be used separately.
I highly recommend PBT 👍 It’s really easy to completely test a function and catch all the edge cases.
c
Nice! I'm likely going to use the assertions (assertSoftly in particular seems great), maybe PBT in the future
My domain has a recursive data structure that's quite complex, so PBT sounds like it could help searching for weird edge cases 👍
a
exactly
c
I guess I'm cursed. I'm trying to migrate my project from
kotlin-test-junit
to
kotlin-test-junit5
. I thought it would be as simple as replacing the dependency, but now the JVM tests do not run anymore.
Is there anything else that needs to be done for Junit 5? The documentation is incredibly incomplete.
a
do you have
useJUnitPlatform()
in Gradle?
c
Nope, thanks 😅
a
I think in theory without
useJUnitPlatform()
the tests will run but you won’t get any console/report output