I've never used mocking frameworks because they tend to be too magical and are normally used as a crutch because your components don't have well defined layers/interfaces.
Instead, the idea is to use Dependency Injection to introduce an interface for the things that your code depends on so you can provide an alternate implementation during testing.
For example, say I was writing some code which uses the clock to time how long a function takes to execute.
First I'd introduce an interface with the behaviour I want to mock out.
use std::time::Instant;
trait Clock {
fn current_time(&self) -> Instant;
}
Then I'll create the implementation that we'll be using in the real application.
/// A [`Clock`] which uses the operating system to determine the time.
struct SystemClock;
impl Clock for SystemClock {
fn current_time(&self) -> Instant {
Instant::now()
}
}
Next I'll write the function that is meant to be tested. This takes something implementing the Clock
trait and the function I want to time.
/// Execute a function and time how long it takes.
fn time_it<C, F>(clock: &C, func: F) -> String
where
C: Clock,
F: FnOnce(),
{
let start = clock.current_time();
func();
let end = clock.current_time();
format!("Executed in {:?}", end - start)
}
Finally I'll create a mock implementation which lets me change the "current time" (now
) whenever I want.
use std::cell::RefCell;
struct MockClock {
now: RefCell<Instant>,
}
impl Clock for MockClock {
fn current_time(&self) -> Instant {
*self.now.borrow()
}
}
And here's a function which uses our system clock and mocked clock.
fn main() {
let system_clock = SystemClock;
let msg = time_it(&system_clock, || {
// Do nothing.
});
println!("Using the system clock: {:?}", msg);
let mock = MockClock { now: RefCell::new(Instant::now()) };
let msg = time_it(&mock, || {
// tell the mock to increment time by 1 second
*mock.now.borrow_mut() += Duration::from_secs(1);
// Do nothing
});
println!("Using the mock clock: {:?}", msg);
}
Compiling and running this on my machine shows it completes in about 3ms.
$ time ./main
Using the system clock: "Executed in 243ns"
Using the mock clock: "Executed in 1s"
./main 0.00s user 0.00s system 84% cpu 0.003 total
(playground)