Skip to content

API change ideas for elegant mocking #33

@Gerschtli

Description

@Gerschtli

Hey,
what do you think of adding a completly new API to improve readability of mockings. I would suggest something like this:

when(namespace::function)
    .expected(times(3))
    .called_with(
        is(equal_to(5)),
        is(none())
    )
    .returns(7);

A call to expected would be optional and defaulting to times(1). Other parameter values could be at(2), al_least(3) etc.

called_with would be a method accepting a hamcrest2 matcher for each parameter. Internal we could simply test these with assert_that!(expected_value, <matcher>). This method call would also be optional, defaulting to empty parameter list.

returns will simply specify, what the function returns. Alternatively runs_original() will delegate the call to the original implementation.

The only thing what I need to call this a full mocking library would be the ability to mock the behavior for struct instances, like:

struct Struct(u8);

#[mockable]
impl Struct {
    fn get(&self) -> u8 {
        self.0
    }
}

fn test() {
    let a = Struct(1);
    let b = Struct(2);

    when(Struct::get).on(&a).returns(10);

    assert_that!(a.get(), is(equal_to(10)));
    assert_that!(b.get(), is(equal_to(2)));
}

It would be a nice feature to be able to configure (via parameter, function call, etc) if calls not specified in expected raise an error and let the test fail, like:

#[mockable]
fn function(a: u32) -> u32 {
    a
}

#[test]
fn test() {
    // order of mocks matter: at first call param is 1, at second param is 2
    when(function)
        .called_with(is(equal_to(1)))
        .returns(5);
    when(function)
        .called_with(is(equal_to(2)))
        .returns(7);
        
    // something like this, to specify mock type
    fail_on_unmocked_calls(function);

    assert_that!(function(1), is(equal_to(5)));
    assert_that!(function(2), is(equal_to(7)));
    
    // following call should let the tast fail
    assert_that!(function(3), is(equal_to(3)));
}

For struct instances, you could specify with anything like the following, where the test would fail on any call to a method of that struct instance.

let a = Struct(1);
fail_on_unmocked_calls(&a);

These are just some ideas inspired by Mockito, PHPUnit and similar mocking libraries. What do you think of it? Would it be something you want to add in this crate?

I would love to help and contribute to your project if so.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions