Mocking library

Hello all.

I have written simple prototype of mocking library. It is very basic, very limited and lacks many features, but I want to get your feedback to know whether my approach is usable at all.

Mocking is implemented using compiler plugin, so nighly Rust is required.

Project is located at GitHub: mockers, you can get short introduction there.
Crates (library and compiler plugin) are uploaded to crates.io: mockers, mockers_macros

Usage:

// air/src/lib.rs

pub trait AirConditioner {
    fn make_hotter(&self, by: i16);
    fn make_cooler(&self, by: i16);
    fn get_temperature(&self) -> i16;
}

pub fn set_temperature_20(cond: &airconditioner) {
    let t = cond.get_temperature();
    if t < 20 {
        cond.make_hotter(20 + t);
    } else {
        cond.make_cooler(t - 20);
    }
}

// air/tests/lib.rs

#![feature(plugin)]
#![plugin(mockers_macros)]

extern crate air;
extern crate mockers;

mock!{
    AirConditionerMock,
    air,
    trait AirConditioner {
        fn make_hotter(&self, by: i16);
        fn make_cooler(&self, by: i16);
        fn get_temperature(&self) -> i16;
    }
}

use mockers::Scenario;

#[test]
fn test_make_hotter() {
    let mut scenario = Scenario::new();
    let cond = scenario.create_mock::<AirConditionerMock>();

    scenario.expect(cond.get_temperature_call().and_return(16));
    scenario.expect(cond.make_hotter_call(4).and_return(()));

    mocked::set_temperature_20(&cond);
}

Running tests:

…
---- test_make_hotter stdout ----
	thread 'test_make_hotter' panicked at 'called `Result::unwrap()` on an `Err` value: "36 is not equal to 4"', ../src/libcore/result.rs:746
…
2 Likes

Neat! Would it be possible to add:

#[mock(AirConditionerMock)]
pub trait AirConditioner {
    // ...
}

so that if the trait is in the same crate, you don't have to copy the whole thing?

For a totally opposite approach, take a look at dtolnay/mock. That one does not use a compiler plugin and does not generate an "AirConditionerMock" struct, but it is limited in may other ways. Here is what it would look like for your example:

#[test]
fn test_make_hotter() {
    let ac = mock!(AirConditioner);
    mock!(ac.get_temperature() -> i16 { 16 });
    mock!(ac.make_hotter(i16) -> ());

    set_temperature_20(ac);
}

I can add support for #[derive(Mock)] directive, this will exclude duplication for your own traits.

I have seen mock crate. It uses very clever mocking technique, however it is inherently unsafe and requires duplication of method signature every time you mock it.

Just-released version 0.3.0 contains support for #[derive(Mock)] and many more features.
I have also added user guide covering basic features.

Please check it out, I need feedback!