Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

sleep-in-tests

Level: warn · Article: Tests

Developers who re-run flaky tests twice before investigating.

What it checks

thread::sleep, tokio::time::sleep or task::sleep inside test code. This rule fires only in tests; sleeping in production code is a different question.

Don’t

#![allow(unused)]
fn main() {
#[tokio::test]
async fn delivers_the_event() {
    let (bus, subscriber) = Bus::with_subscriber();
    bus.publish(Event::UserBanned);
    tokio::time::sleep(Duration::from_millis(50)).await; // "enough time"
    assert_eq!(subscriber.received(), vec![Event::UserBanned]);
}
}

Fifty milliseconds is enough on your laptop. On a loaded CI runner it is not, once a week, and someone adds a zero.

Do

#![allow(unused)]
fn main() {
#[tokio::test]
async fn delivers_the_event() {
    let (bus, mut subscriber) = Bus::with_subscriber();
    bus.publish(Event::UserBanned);
    let received = subscriber.next().await; // waits for the event, not for time
    assert_eq!(received, Some(Event::UserBanned));
}
}

When the code under test measures time, inject the clock and advance it: clock.advance(Duration::from_secs(3600)) is instant and exact.

Silence it

#![allow(unused)]
fn main() {
// rabot: allow(sleep-in-tests) exercises the real timeout path against the in-process server
}