Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

withData in StringSpec tests #3869

Open
Fleshgrinder opened this issue Feb 11, 2024 · 7 comments
Open

withData in StringSpec tests #3869

Fleshgrinder opened this issue Feb 11, 2024 · 7 comments
Labels
data-driven-testing 🗃️ Related to the data driven testing mechanisms within the testing framework.

Comments

@Fleshgrinder
Copy link

It seems as if withData cannot be used in StringSpec tests, unless one accepts that each individual case gets its name assigned automatically. The following, which should work, doesn't compile, because Kotlin cannot infer the correct types.

class DataTest : StringSpec({
    "what this test tests" {
        withData(
            "a" to 1,
            "b" to 2,
            "c" to 3,
        ) { (first, second) ->
            // fails with runtime error
        }
    }

    withData(
        { (first, second) -> "$first <> $second" },
        "a" to 1,
        "b" to 2,
        "c" to 3,
    ) { (first, second) ->
        // doesn't compile, type inference issues
    }
})

The @BuilderInference would fix the second issue:

@OptIn(ExperimentalTypeInference::class)
@BuilderInference
fun <T> RootScope.withData( // ...
@Kantis
Copy link
Member

Kantis commented Feb 12, 2024

Re: 1st issue. I believe you should be unable to invoke withData from within a leaf scope.

You could switch to FreeSpec which is similar to StringSpec but allows nesting and create a container scope, like:

class DataTest : FreeSpec({
    "what this test tests" - {
        withData(
            "a" to 1,
            "b" to 2,
            "c" to 3,
        ) { (first, second) ->
        }
    }
}

IMO we should try to add a @DslScopeMarker to prevent code in 1st example from compiling.

Re 2nd issue: Will have to read up what that does.. I'm not sure it's a good idea to force opt-in to use withData in root scope?
A workaround could be to use a named param for the 1st argument.

class DataTest : StringSpec({
        withData(
        nameFn = { (first, second) -> "$first <> $second" },
        "a" to 1,
        "b" to 2,
        "c" to 3,
    ) { (first, second) ->
    }
}

@Fleshgrinder
Copy link
Author

Fleshgrinder commented Feb 13, 2024

This is what I added to my fixtures:

// https://github.com/kotest/kotest/issues/3869
@file:OptIn(ExperimentalTypeInference::class)

package com.fleshgrinder.kotest

import io.kotest.common.ExperimentalKotest
import io.kotest.core.names.TestName
import io.kotest.core.spec.style.StringSpec
import io.kotest.core.spec.style.scopes.ContainerScope
import io.kotest.core.spec.style.scopes.addTest
import io.kotest.core.test.TestType.Dynamic
import kotlin.experimental.ExperimentalTypeInference

@BuilderInference
@OptIn(ExperimentalKotest::class)
fun <T> StringSpec.withData(
    name: (T) -> String,
    params: Array<out T>,
    test: suspend ContainerScope.(T) -> Unit,
) = params.forEach { addTest(TestName(name(it)), false, null, Dynamic) { test(it) } }

@BuilderInference
inline fun <reified T> StringSpec.withData(
    noinline name: (T) -> String,
    param: T,
    vararg params: T,
    noinline test: suspend ContainerScope.(T) -> Unit,
) = withData(name, arrayOf(param, *params), test)

The @file:OptIn(ExperimentalTypeInference::class) is only required in this file, not in my tests, if that's what you are afraid of.

Having a @DslMarker would help to ensure that one doesn't try to use it from within a test, absolutely.

And, yes, using nameFn = { "" } also resolves the issue.

@Kantis
Copy link
Member

Kantis commented Feb 14, 2024

I'm more afraid of the experimental type inference to be dropped / broken in some upcoming Kotlin version, at which point we would have to drop the feature (breaking change) or lock our users to a certain Kotlin version. Or could that not happen?

Do you know how long it's been around and if there are any fopen YouTrack tickets discussing it?

@Fleshgrinder
Copy link
Author

It's used in the standard library, I even added functions to it using it. The annotation is for sure going to be dropped at some point, but only because K2 is going to be intelligent enough on its own without it.

@Kantis
Copy link
Member

Kantis commented Feb 15, 2024

Ok, I think we could add it. Wdyt @sksamuel ?

@LeoColman LeoColman added the data-driven-testing 🗃️ Related to the data driven testing mechanisms within the testing framework. label Mar 3, 2024
@sksamuel
Copy link
Member

I'm in favor, if we can get it to work. I tried adding

@OptIn(ExperimentalTypeInference::class)
@BuilderInference

To all the withData functions and could not get the 2nd example to compile.
I believe it is still getting confused between the nameFn version and the version that takes var args.

@sksamuel
Copy link
Member

Might be related to the ticket I filed at #3901

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
data-driven-testing 🗃️ Related to the data driven testing mechanisms within the testing framework.
Projects
None yet
Development

No branches or pull requests

4 participants