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

row toString() considered "unstable" even if all type parameters are clearly stable #3947

Open
hakanai opened this issue Mar 20, 2024 · 0 comments · May be fixed by #4007
Open

row toString() considered "unstable" even if all type parameters are clearly stable #3947

hakanai opened this issue Mar 20, 2024 · 0 comments · May be fixed by #4007
Assignees
Labels
data-driven-testing 🗃️ Related to the data driven testing mechanisms within the testing framework. enhancement ✨ Suggestions for adding new features or improving existing ones.

Comments

@hakanai
Copy link

hakanai commented Mar 20, 2024

Which version of Kotest are you using
5.8.1

Background

I was trying to use withData to spin off multiple very similar tests.

The documented way to use it is to make a data class for the row objects. I have a lot of tests, I am very lazy, and having to define the types of the row objects for every test is just tons of repetition. And these row types effectively never get to be reused, because the types inside them always differ. And worst of all, I have to come up with names for them every single time. Which is even more repetition. Which is even more repetition.

So I started writing generic classes for them, like Row1<A>, Row2<A,B>, Row3<A,B,C>, Row4<A,B,C,D>. And then a bunch of factory functions all called row(...) so you didn't have to type the number of parameters over and over either.

And then I thought, wait a second, doesn't kotest already have something like this?

So I tried using the existing io.kotest.data.row(...), which returns classes with the exact same names I was using, and which are defined pretty much the exact same way as well.

So, for example, one of my tests looks like:

    "argument" - {
        withData(
            row(-3, 2, "2.55359005004222568722", "top left quadrant"),
            row(-3, -2, "-2.55359005004222568722", "bottom left quadrant"),
            row(3, -2, "-0.58800260354756755125", "bottom right quadrant"),
            row(3, 0, "0.00000000000000000000", "positive real"),
            row(-3, 0, "3.14159265358979323846", "negative real"),
            row(0, 2, "1.57079632679489661923", "positive imaginary"),
            row(0, -2, "-1.57079632679489661923", "negative imaginary"),
            row(0, 0, "0.00000000000000000000", "zero"),
        ) { (x, y, expected, _) ->
            Complex(Real.valueOf(x), Real.valueOf(y)).argument shouldBeCloseTo expected
        }
    }

This seems like it should be perfectly fine, but...

Problem

When using row(...) to specify the rows for data-driven tests, all the test names come out as just the class name for the row object. In this case, io.kotest.data.Row4.

In the logs, there are many of these:

Warning, type class io.kotest.data.Row4 used in data testing does not have a stable toString()
Warning, type class io.kotest.data.Row4 used in data testing does not have a stable toString()
WARN: Duplicated test name io.kotest.data.Row4. To disable this message, set DuplicateTestNameMode to Silent.

It seems that kotest tries to detect whether the toString() method is stable, i.e. returns the same value when called multiple times for the same test. Because Row4<A,B,C,D> is generic, any of its types could be an array, or something else which doesn't toString() sensibly.

This is completely nuts for my case though, because I am using solely Int and String. There is no way in hell the toString() for those is unstable, and if I replace the data class with one where the types are hard-coded, it works fine.

Potential Solutions

Option A. Add @IsStableType to RowN classes and call it a day. The issue is, someone might feed in something like arrays, which was presumably the reason it was disabled in the first place. (This is probably what I will do for my own workaround - keep the boilerplate classes I have now, add the annotation, bam, all the tests will work. If I do something stupid like adding an array, it would break, so I'd just aim not to be stupid.)

Option B. Actually look at the types of the objects in the RowN object when detecting whether it's stable. Consider primitives and strings as stable and then think about whether other types are or not. Maybe consider anything annotated with @IsStableType as stable as well. I don't know whether some kind of reified types hack could be used to do this, or whether you'd just have to look at the runtime values. But I guess since the important thing is the behaviour of toString(), looking at the runtime values should be totally fine.

A Side Note

If the basic forAll way of doing data tests had properly come up as separate test failures/successes in IDEA, I never would have needed to try withData. It sucks that the former didn't "Just Work" in this situation. It has been a real long day.

@LeoColman LeoColman added enhancement ✨ Suggestions for adding new features or improving existing ones. data-driven-testing 🗃️ Related to the data driven testing mechanisms within the testing framework. labels Mar 20, 2024
@Kantis Kantis self-assigned this May 9, 2024
Kantis added a commit that referenced this issue May 9, 2024
By inspecting the stability of the actual values, we can see beyond the
generics of the data class.

Fixes #3947
Kantis added a commit that referenced this issue May 9, 2024
By inspecting the stability of the actual values, we can see beyond the
generics of the data class.

Fixes #3947
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. enhancement ✨ Suggestions for adding new features or improving existing ones.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants