Does anybody know if there is a typesafe version o...
# kotest
d
Does anybody know if there is a typesafe version of
shouldBe
? It's all too easy to accidentally compare apples and oranges since
shouldBe
just accepts any type as its argument
1
c
There is none 😕
m
What about shouldBeEqual?
k
shouldBeEqual is not typesafe either.
m
Hm.
Copy code
infix fun <A : Any> A.shouldBeEqual(expected: A): A
Looks type safe to me though. Or am I missing something now?
k
The type parameter
A
will become the closest common superclass/interface between the two arguments. And all types have at least
Any
as a superclass.
1
d
I guess we could force it by using
Copy code
"foo".shouldBeEqual<String>("foo")
But that doesn't look really appealing
c
This is why other assertion frameworks use two-parts assertions, for example with strikt:
Copy code
expectThat(foo) isEqualTo "bar"
the first function "captures" the type, so the compiler is not allowed to up-cast to Any to match the second argument.
1
d
Ah I see, I still like Kotest's style better so I'll use it and accept this weird quirk
k
I quite like Strikt's
expectThat
and AssertK's
assertThat
because they clearly show the start of an assertion on any particular line. With Kotest, you need to scan with your eye to find a shouldXxx somewhere in the middle of a line, and only then do you know that it's an assertion.
c
It depends on the style. With Kotest, I tend to write many very, very short tests that only assert a very specific thing. When tests are 5 lines long, there's not really a doubt what the assertion is. With JUnit + strikt, I tend to write longer tests that assert multiple things, taking advantage of the combined expression message.
d
It's a matter of taste I guess, for me Kotest's style feels more natural and less cluttered, but that's also because I usually follow a given-when-then top-down approach in my test methods, so I know the bottom part always only has assertions
1
e
I started writing an inspection for
shouldBe
when comparing definitely incompatible types. Would that be an acceptable middle ground?
The problem with forcing type consistency is for cases where
shouldBe
actually treats different types as compatible, for instance comparing an array with a list
1
d
An inspection would be acceptable, I don't think we can do much better
e
Maybe the inspection could also be added as a detekt-rule, so it could be enforced in CI. Are you using detekt?
d
Not at the moment
l
I think we did have a discussion at some point of using Kotlin Contracts to ensure the type comparison, but the API wasn't ready for that yet. Maybe it is now?
I'm usually ok with comparing Apples to Oranges because usually the runtime will tell me "Hey dummy, you're comparing wrong". That is sometimes annoying to debug when
toString()
is the same for both classes, but usually I catch the type unsafety pretty quickly
I agree it's a 'quirk'.
shouldBe
kinda sacrifices type safety for being more dynamic
k
Type safety is not as important in test code as it is in production code though, is it?
d
That's true, what I do like about
shouldBe
is that it's basically the most concise, yet still clear, version of
if(this != that) throw Something()