Detached panic detection
When a test spawns threads or async tasks that outlive the test body, panics in those detached contexts are normally invisible — the test passes while the panic message is silently printed to stderr. test-r can detect these panics and fail the test, but only when using test-r’s own spawn functions.
Spawn functions
test-r provides two spawn functions that propagate test context and capture panics:
test_r::spawn_thread— wrapsstd::thread::spawntest_r::spawn— wrapstokio::spawn(requires thetokiofeature)
These are drop-in replacements with the same signatures:
#![allow(unused)]
fn main() {
use test_r::{test, spawn_thread};
#[test]
fn test_with_background_thread() {
let handle = spawn_thread(|| {
// If this panics, the test will fail
assert_eq!(2 + 2, 4);
});
handle.join().unwrap();
}
}
#![allow(unused)]
fn main() {
use test_r::{test, spawn};
#[test]
async fn test_with_spawned_task() {
let handle = spawn(async {
// If this panics, the test will fail
assert_eq!(2 + 2, 4);
});
handle.await.unwrap();
}
}
How it works
By default, every test uses DetachedPanicPolicy::FailTest. When you use spawn_thread or spawn:
- The current test’s identity is propagated to the new thread or task.
- The closure is wrapped in
catch_unwindto intercept any panic. - If a panic occurs, it is recorded and associated with the originating test.
- After the test body completes, the runner checks for collected panics and fails the test if any were found.
Important: If you use
std::thread::spawnortokio::spawndirectly, panics in those threads/tasks will not be detected by test-r. They will be printed to stderr by the default panic hook but will not cause the test to fail.
Opting out
If a test intentionally spawns work that may panic, you can disable detection with the #[ignore_detached_panics] attribute:
#![allow(unused)]
fn main() {
use test_r::{test, ignore_detached_panics, spawn_thread};
#[ignore_detached_panics]
#[test]
fn test_that_expects_detached_panics() {
spawn_thread(|| {
panic!("this is expected");
});
}
}