willowtreeapps / assertk

assertions for kotlin inspired by assertj
MIT License
757 stars 84 forks source link

Naming a failure #526

Open JakeWharton opened 4 months ago

JakeWharton commented 4 months ago

Maybe I'm overlooking something, but I can't seem to name an assertFailure with any clarifying data.

I have a test function calling a helper with a bunch of example failing cases. When one does not fail, I just get an "Expected failure but was success" with no real useful information. On the JVM the trace can help me, but this failure is JS-specific which means I get a near-useless trace.

Something like:

@Test fun parses() {
  assertParseFailure("1")
  assertParseFailure("1.2")
  assertParseFailure("1.2.a")
  assertParseFailure("1.2.3.4")
  assertParseFailure("1.2.3-beta")
}
private fun assertParseFailure(version: String) {
  assertFailure { parse(version) }
    .isIntanceOf<ISE>()
    ...
}

I have two proposals:

First, just let me name an assertFailure and put it in the message. Easy!

Second, and a bit weirder, but what if there was an Assert-based failure-expecting version of prop/#521? If there was, I could do

assertThat(version)
  .havingFailure("parses", ::parse)
  .isInstanceOf<ISE>()
  ...

I haven't really thought through the implications of such a thing. And in general I'm not sure to what degree the outcome of #521 is solely for traversing "down" an instance vs. processing it with other types/functions.

Something to think about, though. Even if it's wrong for this specific situation (because proposal 1 seems obvious to do), maybe it's a thing that's still needed for actual instance-based property/function calls that we expect to throw?

evant commented 4 months ago

Yeah I'd expect assertFailure to have the same additional arguments as assertThat. Also, we still have the result assertions so

assertThat(runCatching { parse(version) }, name = version).isFailure()

should work as a work-around?

dalewking commented 4 months ago

Ran into this myself and am working around it by adding this to my code:

inline fun assertFailure(description: String? = "lambda", f: () -> Unit): Assert<Throwable> {
    @Suppress("TooGenericExceptionCaught") // Intentionally capturing all exceptions.
    try {
        f()
    } catch (t: Throwable) {
        return assertThat(t, "exception thrown from $description")
    }
    fail("expected failure but $description completed successfully")
}