pub trait Unmock: MockFn { }
Expand description

MockFn with the ability to unmock into a unique true implementation.

A true implementation must be a standalone function, not part of a trait, where the first parameter is generic (a self-replacement), and the rest of the parameters are identical to MockInputs::Inputs:

#[unimock(unmocked=[my_original(self, a)])]
trait DoubleNumber {
    fn double_number(&self, a: i32) -> i32;
}

// The true implementation is a regular, generic function which performs number doubling!
fn my_original<T>(_: T, a: i32) -> i32 {
    a * 2
}

The unmock feature makes sense when the reason to define a mockable trait is solely for the purpose of inversion-of-control at test-time: Release code need only one way to double a number.

Standalone functions enables arbitrarily deep integration testing in unimock-based application architectures. When unimock calls the true implementation, it inserts itself as the generic first parameter. When this parameter is bounded by traits, the original fn is given capabilities to call other APIs, though only indirectly. Each method invocation happening during a test will invisibly pass through unimock, resulting in a great level of control. Consider:

#[unimock(unmocked=[my_factorial(self, input)])]
trait Factorial {
    fn factorial(&self, input: u32) -> u32;
}

// will it eventually panic?
fn my_factorial(f: &impl Factorial, input: u32) -> u32 {
    f.factorial(input - 1) * input
}

assert_eq!(
    120,
    // well, not in the test, at least!
    mock([
        Factorial__factorial.stub(|each| {
            each.call(matching! {(input) if *input <= 1}).returns(1_u32); // unimock controls the API call
            each.call(matching!(_)).unmocked();
        })
    ])
    .factorial(5)
);

Implementors