Open grodin opened 3 months ago
Agreed that this could use some work. I'm going to try to explain what's there now as a starting point, which is sadly under-documented. Note: the examples below will be using some of the api's directly are usually would be implemented inside an assertion.
You can pass a name to assertions. They show up at the beginning and may built in assertions will append to it. Note: the api for this is fairly manual, i.e. you need to explicitly append to it when chaining, otherwise it'll only show the name you pass in.
ex:
assertThat(outer, name = "outer").assertThat(inner, name = "inner").someAssertion()
// -> [inner] assertion failed
assertThat(outer, name = "outer").run { assertThat(inner, name = appendName("inner", separator = ".")) }.someAssertion()
// -> [outer.inner] assertion failed
many built-in assertions will append to this, ex: prop
does appendName(name, separator = ".")
and index
does appendName("[$index]")
This is something that's implicitly tracked and is appended to the end of the assertion. The purpose for this to know what you were originally asserting on through chaining. The logic here is that if you aren't chaining nothing will be shown, if you are, the outer subject will be carried through and appended to the message.
assertThat(outer).someAssertion()
// -> assertion failed
assertThat(outer).assertThat(inner).someAssertion()
// -> assertion failed (outer)
For completeness, you can control how this value is rendered with displayActual
. The intention for this was for cases where you subject did not have a useful toString() implementation, but https://github.com/willowtreeapps/assertk/issues/513 correctly points out the behavior of this parameter is quite confusing.
assertThat(outer, displayActual = { "changed" }).someAssertion()
// -> assertion failed
assertThat(outer, displayActual = { "changed" }).assertThat(inner).someAssertion()
// -> assertion failed (changed)
In assertJ they have something like this:
assertThat(5 + 19)
.describedAs("The sum of 5 and 9")
.isEqualTo(14)
I think there is a library I have used that instead of adding the description to the assert it added it to the value with a wrapper class:
val actual = (5 + 9).describedAs("The sum of 5 and 9")
assertThat(actual).isEqualTo(14)
There are a number of places in the API where it would be good to add a String parameter to name something or add some context. Some quick examples from existing open issues:
eachHaving()
#533 (this is a name rather than more general context but it's the same basic idea)Truth.assertWithMessage()
#514, #352all()
andany()
this issue commentIt seems like there are two main threads to these:
eachHaving()
should have a name param to matchhaving()
)I thought it would be useful to open an issue to kind of unify discussion around these ideas.
It would potentially be useful to go through the whole API and try to find the places where a string parameter seems to be wanted and try to design a consistent approach, rather than dealing with each request in an ad-hoc way.
Point 1. seems fairly easy to address, so I'll try to start collecting instances of that.