Tom
Posted on January 2, 2024
I've got something cooking that i've written in Rust and I recently discovered a neat trick to test it.
Coming from python, mocks are a thing i often reach for. I find it really neat that I can throw an api from a package in a test harness so easily, same as how I'd do with a HTTP api in an integration test.
Something external I often mock is time. That's not some deep nihilist philosophy, i mean literally if there's a delay i'll mock out stuff that waits, sleep, etc so that my tests can prove the delayed behaviour instantly.
In the Rust thing I'm working on I faced this exact issue, specifically a timeout on a socket.
I have a check on an Instant
, is .elapsed()
greater than a Duration
, and if it is I drop the socket from the hashmap it's stored in and it gets closed.
But I don't want my tests to wait the actual time, I want to be able to manipulate that time.
In python I would move the external world of the time api around the timeout internal to my function, but in Rust I can use an impl inside my tests to flip that and just move my timeout internal to my struct around the external world. check this out:
struct WaitingPlace {
last_switch: Instant,
switch_every: Duration,
is_on: bool,
}
impl WaitingPlace {
fn new(switch_every: Duration) -> Self {
Self {
last_switch: Instant::now(),
switch_every,
is_on: false,
}
}
fn do_stuff(&mut self) {
self.flip_check();
println!("is_on: {}", self.is_on);
}
fn flip_check(&mut self) {
if self.last_switch.elapsed() > self.switch_every {
self.last_switch = Instant::now();
self.is_on = !self.is_on;
}
}
}
#[cfg(test)]
mod tests {
fn test_time() {
use super::WaitingPlace;
impl WaitingPlace { // <- here's the magic where I reach into the struct and change the time
fn move_time(&mut self, last_switch: Instant) {
self.last_switch = last_switch;
}
}
let mut wp = WaitingPlace::new(Duration::new(1, 0));
assert_eq!(wp.is_on, false);
wp.do_stuff();
assert_eq!(wp.is_on, false);
wp.move_time(Instant::now() - Duration::new(1, 0));
wp.do_stuff();
assert_eq!(wp.is_on, true);
}
}
it's effectively a window onto the internals of my WaitingPlace struct that only exists in my tests. pretty neat i reckon. Maybe this is super obvious to you seasoned rust devs, or maybe i've created an abomination that no one wants to see. Either way i thought it was neat.
If you know a better way of doing this let me know.
Posted on January 2, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.