Introducing mock_me crate for mocking/injecting functions during testing


#1

TLDR: I made a (hopefully) easy way to mock functions when writing unit tests using procedural macros, I will include a code example at bottom of this post.

Recently, while I was working on a Rust webserver, I was frustrated by the lack of an easy way to mock functions (mainly db calls) when writing unit tests.

Eventually, I settled on a somewhat unsatisfying and unstructured approach using adhoc conditional compilation, as I have documented here Artisanal Mocks.

I have made this crate in order to make this whole process easier, hopefully you like it. I feel that it provides an unmet need for testing Rust code. Here is the code example on how to use it from the readme:

 #![feature(proc_macro)]
 extern crate mockme;
 use mockme::{mock, inject};

 // Below we will create two mocking identifiers called id_1 and id_2.
 // We will then provide the name of the two functions we are mocking, as well as
 // their type signature. In future iterations, hopefully the signature won't be needed.
#[mock(id_1="external_db_call: fn(u32) -> String", id_2="other_call: fn() -> String")]
fn my_super_cool_function() -> String {
    let input = 42u32;
    // external_db_call will be replaced with fake_mocked_call during testing
    let db_result = external_db_call(input);

    // other_call will also be replaced
    let other_result = other_call();
    format!("I have two results! {} and {}", db_result, other_result)
}

 // Finally, when we run our tests, we simply need to provide the identifier we previously used,
 // as well as the name of the replacement function
#[test]
#[inject(id_1="db_fake", id_2="other_fake")]
fn actual_test2() {
    let result = my_super_cool_function();
    assert_eq!(result, "I have two results! Faker! and This is indeed a disturbing universe.");
}

fn db_fake(_: u32) -> String { "Faker!".to_string() }
fn other_fake() -> String { "This is indeed a disturbing universe.".to_string() }

This library is still a work in progress, so I would appreciate any feedback and/or contributions. I do not consider myself a Rust expert, so I am sure there are ways to improve what I did.

Also, fun bonus trivia fact: this is the first time that I have really needed to use an unsafe block in my code.