Hi all, possibly crazy idea here, but:
Could we (mis)use the new v2 Try
trait to improve the ergonomics of unit tests that involve fallible code? Currently it's rather annoying, and there seem to be two main approaches that both have drawbacks:
Approach 1 - lots of unwrap
and/or expect
:
#[test]
fn my_test() {
let arg = might_fail().unwrap();
let value = also_might_fail(arg).unwrap();
let final_result = again_might_fail(value).unwrap();
assert_equal!(final_result, 10);
}
Approach 2 - lots of ?
and returns Result
:
#[test]
fn my_test() -> Result<(), Box<dyn Error>> {
let arg = might_fail()?;
let value = also_might_fail(arg)?;
let final_result = again_might_fail(value)?;
assert_equal!(final_result, 10);
Ok(())
}
Approach 1 is nice because unit test failures are easily traced back to the line of code that failed (possibly needs backtracing enabled). Not nice, because all those unwrap
calls bloat the code and make it ugly.
Approach 2 is the opposite -- much cleaner (normal looking) code, but it needs to return a Result
now and thus loses the backtrace. All you know is the error type, not where it came from. Doing tricks with map_err
could help localize the error better, but that's even more bloated than unwrap
.
So here's the crazy idea: Only under #[cfg(test)]
, provide an impl Try for ()
, with a corresponding impl<E: Debug> FromResidual<Result<Infallible, E>> for ()
whose from_residual
panics just like Result::unwrap
would. This playground example, using a FakeUnit
newtype as a proxy for ()
to avoid triggering orphan rules, suggests the standard library could do it.
Doing so would allow the best of both worlds in a unit test -- unit return but able to use ?
:
#[test]
fn my_test() {
let arg = might_fail()?;
let value = also_might_fail(arg)?;
let final_result = again_might_fail(value)?;
assert_equal!(final_result, 10);
}
Potentially useful way to improve unit test ergonomics? Or just plain crazy/silly/evil?