https://kotlinlang.org logo
#kotest
Title
# kotest
w

wasyl

05/10/2021, 11:16 AM
I’m wondering why I don’t see compilation errors for assertions where types don’t match, for example
5 shouldBe "5"
. There’s no error shown, even though the signature would force the right-hand parameter to be a assignable to the left-hand one (
infix fun <T, U : T> T.shouldBe(expected: U?)
), or so I thought. Is this expected? It’s pretty annoying when refactoring code, because tests still compile but fail in runtime
I feel like I’m missing something obvious
I guess it falls back to
Any.shouldBe(other: Any)
?
j

Javier

05/10/2021, 11:22 AM
Why it uses two generics instead of only T?
w

wasyl

05/10/2021, 11:25 AM
Probably to support things like
foo shouldBe fooSubtype
? But even with single generic parameter it seems
A() shouldBe C()
(no inheritance) works, because now
T
is just
Any
as well
I “worked around” this by creating a custom function in kotest package:
Copy code
infix fun Any.shouldBe(expected: Any?): Nothing = error("Unsupported")
but it only works for
shouldBe
, I’d have to have overloads for all the different matchers 😕
s

sam

05/10/2021, 11:38 AM
There's a reason for this but I can't remmeber atm. Something to do with shouldBe accepting matchers as well as types iirc
w

wasyl

05/10/2021, 11:44 AM
Is it even possible to have a signature that errors out if both
T
and
U
would be
Any
?
j

Javier

05/10/2021, 11:49 AM
But for specific type there is a function
shouldBeTypeOf
I think
w

wasyl

05/10/2021, 11:51 AM
I don’t want to check just types. Here’s the issue: I had a test
doSomething() shouldBe 5
. Later on I change
doSomething
to return a string instead of an int. Normally it’s easy to find all the places to update by just trying to compile the project and navigating to each error
However,
shouldBe
will not result in an error. I still want to do some meaningful change, in this case check that
doSomething shouldBe "5"
, not just check a type
My issue is that Kotest assertions don’t support refactoring like that very well, because “obvious” errors like this only show up when running the tests. Unless I’m checking
Copy code
// foo: Any
// bar: Any
foo shouldBe bar
I don’t want
shouldBe
to resolve generic parameters to
Any
s

sam

05/10/2021, 11:53 AM
There is an annotation called onlyinputtypes
But not sure it's public
I agree with you lukasz but if it was an easy change I'd have done it :)
w

wasyl

05/10/2021, 11:55 AM
Yeah sure, originally I didn’t really understand why this happens, only later I realized it’s because type is
Any
. And I agree it’s not easy, other than overloading each matcher with a deprecated
Any.
variant I don’t have a clue what can be done
I may try creating a custom Android Lint check for our project, as it shows up in IDE
s

sam

05/10/2021, 11:57 AM
Not that 1.5 is out I will have another play
The compiler may be smarter now
j

Javier

05/10/2021, 11:58 AM
@wasyl I know you don't want to check the type, what I mean is
shouldBe
should not compile if the type is different, and maybe a
relaxedShouldBe
or something so should be added
👌 1
w

wasyl

05/10/2021, 11:59 AM
OnlyInputTypes
might be helpful, although I don’t have it handy to check how it works exactly. But I see it in
kotlin-test
sources
s

sam

05/10/2021, 12:00 PM
It was private to jb previously
This topic has come up several times before and I'd love to fix it
w

wasyl

05/10/2021, 12:01 PM
Yep it’s still
internal
s

sam

05/10/2021, 12:02 PM
There's also the issue of subtyping on both sides
dog shouldBe animal
animal should be dog
w

wasyl

05/10/2021, 12:03 PM
Right 😕
shouldBe
is symmetric
Or, should be
s

sam

05/10/2021, 12:03 PM
Yep
Now we could add shouldEqual which isnt
Or something
w

wasyl

05/10/2021, 12:05 PM
Actually Javier had a point, why are there two types? Isn’t
shouldBe
only true when both sides are exactly the same type?
j

Javier

05/10/2021, 12:05 PM
I think there is no problem when
animal shouldBe dog
not compiling
It is like avoid anticipating to the future
s

sam

05/10/2021, 12:05 PM
Both sides can be bounded to each other
CharSeq shouldBe String
String shouldBe CharSeq
j

Javier

05/10/2021, 12:06 PM
First fails, second works
☝️ 1
And I like that
s

sam

05/10/2021, 12:07 PM
Right but the 5 years worth of code using it might not like it ;)
j

Javier

05/10/2021, 12:07 PM
you force to have an standard in the order to check the things
s

sam

05/10/2021, 12:08 PM
The order is actual shouldBe expected
Things that previously compiled must continue to compile. I'd be happy to add shouldEqual or something like that which uses the lhs type as the upper bound and does not support arbitrary matchers.
j

Javier

05/10/2021, 12:11 PM
In JUnit is expected/actual no?
s

sam

05/10/2021, 12:11 PM
not sure, but I just follow the english readability "my cat should be black"
error: your cat is purple
shouldEqual
would be trivial to implement and easy to do a find replace to update existing usages for @wasyl
j

Javier

05/10/2021, 12:12 PM
but it is
myCat shouldBe black
is
String shouldBe CharSeq
myCat
knows what is color (inherence, property, or whatever), black is a color, it knows nothing about animals or cats
exactly the same that String and CharSeq
s

sam

05/10/2021, 12:13 PM
That's only the type defs though, the actual impls could be the same val cat = Cat("felix") val cat2: Animal = Cat("smokey") cat shouldBe cat2
You have a method that returns a super type and you want to test its the actual type you expect in your unit tests. We don't really want to cast in the test either.
j

Javier

05/10/2021, 12:13 PM
that should compile, but cat2 shouldBe cat, should not compile
s

sam

05/10/2021, 12:13 PM
they should both compile imo
w

wasyl

05/10/2021, 12:14 PM
And what about overriding
Any.shouldBe
in a separate, opt-in artifact?
s

sam

05/10/2021, 12:14 PM
anyway the point is moot, because like I said, if we change it, we will break code for people who have been using kotest (and kotlintest) since 2016. So the best workaround is a new method that is stricter on its types.
j

Javier

05/10/2021, 12:14 PM
the problem adding something like strictShouldBe it can be used as default in almost all the case
that is the reason I think it is worth shouldBe being strict, and having a relaxedShouldBe
s

sam

05/10/2021, 12:15 PM
That would be better, but it's not going to change in a backwards incompatible way now outside of a major major version bump
j

Javier

05/10/2021, 12:15 PM
Yeah, maybe for 5.0 or so
w

wasyl

05/10/2021, 12:15 PM
Anyway the issue would be present for all kinds of matchers, like
aListOfFoo shouldContain bar
should fail as well
s

sam

05/10/2021, 12:16 PM
kotlin doesn't have higher kinded types so there's plenty of restrictions
2 Views