Pihentagy
03/22/2024, 2:41 PMSzymon Jeziorski
03/22/2024, 3:33 PMcallsOriginal()
method within answers
block
Example:
var returnedResult: String? = null
every {
myService.foo()
} answers {
callOriginal().also { returnedResult = it }
(invoking tested logic)
assertThat(returnedResult).isEqualTo("hello")
Pihentagy
03/22/2024, 3:42 PMPihentagy
03/22/2024, 3:43 PMSzymon Jeziorski
03/22/2024, 3:54 PMfoo(bar: String): String
and wanted to act on passed bar
argument (or arguments in case you used MutableList for capturing slot).
Unfortunately isEqualTo
method from AssertJ just accepts Object
as argument so it's the API itself that lacks type-safety.
I used it just for the example, but you may use any other assertion framework which has more type-safe APIs
(I very much dislike AssertJ for that very thing, I'm still using it in work projects as it has the most extensive API I've found which can simplify many edge cases (especially for recursive comparisions, using custom comparators etc))Pihentagy
03/22/2024, 4:17 PMSzymon Jeziorski
03/22/2024, 4:45 PMassertThat("string")
then resulting type is AbstractStringAssert<*>
which has type information, but often when you use little more advanced constructs from AssertJ then type information is lost somewhere in between:
// extension method to make API a little more kotlin-friendly
inline fun <reified T : Any> RecursiveComparisonAssert<*>.withComparatorForType(
noinline comparator: (T, T) -> Int
): RecursiveComparisonAssert<*> = withComparatorForType(comparator, T::class.java)
// just an example to showcase API, of course could be asserted with simplier API
class ComplexObject(val name: String, val height: BigDecimal) {
val id = UUID.randomUUID().toString()
}
@Test
fun `assertJ test`() {
val complexObject = ComplexObject("sampleName", BigDecimal("1.20"))
val expectedObject = ComplexObject("sampleName", BigDecimal("1.200"))
assertThat(complexObject) // AbstractObjectAssert<ComplexObject>
.usingRecursiveComparison() // RecursiveComparisonAssert<*> - type information already lost
.withComparatorForType(BigDecimal::compareTo) // ensuring scales are not affecting assertion result
.ignoringFields(ComplexObject::id.name) // ignoring field not being cared about
.isEqualTo(expectedObject)
}
I could write extensions for cases where type information is preserved, and that might be a good idea ineed, but that wouldn't work for more advanced cases.
And for your case with decompiled source code - AbstractStringAssert has isEqualTo(String expected)
method, but in the same time it inherits from AbstractAssert
which has base method isEqualTo(Object expected
). Therefore invoking isEqualTo
with not-compatilble argument type-wise would just fall back to it, and that's what you experienced