fn main() { println!("hello."); } /// panic if passed `false`: /// fn dont_panic(really: bool) -> i32 { if !really { panic!("Aaaaaaaah!"); } 42 } #[test] fn test_cause_a_panic() { assert_eq!( dont_panic(true), 42 ); // Now check that dont_panic(false) really does panic: let my_result = std::panic::catch_unwind( || dont_panic(false) ); assert!(my_result.is_err()); assert_eq!( dont_panic(true), 42 ); /* Explanation: the `||` has NOTHING to do with boolean-OR; * It is creating an anonymous function. * In this case, an anonymous function that takes 0 inputs (a "thunk"). * It's a classic way to have a chunk of code that you want somebody else to run. * * The problem if we just call `dont_panic(false)` is that control will never reach * `assert!` -- the panic(exception) will have already occurred! * So instead we'll wrap our code-that-might-panic inside a thunk, * and give that thunk to somebody else and let them invoke it. (Kinda like a hot potato.) * * So catch_unwind takes our thunk, and (you can imagine) calls it inside a try/catch. * - if the provided code runs normally and returns some value `r`, catch_unwind returns `Ok(r)` * - if the provided code panics, it catches that panic and wraps it into an `Err(msg)`. * * Btw, in Java/JUnit `assertThrows` also has to be passed a thunk * (`java.util.concurrent.Callable` or `java.util.function.Supplier`). * * Minor note: the panic still prints a message, BUT the program continues. * Though you won't see that message if you're in 'test' mode, * since that hides stdout by default. * * * tl;dr: to test code you *expect* to panic: * precede it with `||` to turn it into a function-of-0-inputs, * pass that function to catch_unwind, * and then assert that catch_unwind returned an Err. */ // This can be done on a single-line, of course: assert!( std::panic::catch_unwind( || dont_panic(false) ).is_err() ); // h/t https://stackoverflow.com/a/42649833/320830 user U007D }