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.
Top comments (0)