use unimock::*;
use async_trait::async_trait;
use std::any::Any;
mod unmock_simple {
use super::*;
#[unimock(api=SpyableMock, unmock_with=[repeat, concat])]
trait Spyable {
fn repeat(&self, arg: String) -> String;
fn concat(&self, a: String, b: String) -> String;
}
fn repeat(_: &impl Any, arg: String) -> String {
format!("{arg}{arg}")
}
fn concat(_: &impl Spyable, a: String, b: String) -> String {
format!("{a}{b}")
}
#[test]
fn works_with_empty_partial() {
assert_eq!(
"ab",
Unimock::new_partial(()).concat("a".to_string(), "b".to_string())
);
}
#[test]
#[should_panic(
expected = "Mock for Spyable::concat was never called. Dead mocks should be removed."
)]
fn works_with_a_partial_having_a_stub_with_non_matching_pattern() {
assert_eq!(
"ab",
Unimock::new_partial(SpyableMock::concat.stub(|each| {
each.call(matching!("something", "else"))
.panics("not matched");
}))
.concat("a".to_string(), "b".to_string())
);
}
#[test]
fn returns_the_matched_pattern_if_overridden() {
assert_eq!(
"42",
Unimock::new_partial(SpyableMock::concat.stub(|each| {
each.call(matching!("a", "b")).returns("42");
}))
.concat("a".to_string(), "b".to_string())
);
}
#[test]
fn works_on_a_mock_instance_with_explicit_unmock_setup() {
assert_eq!(
"ab",
Unimock::new(SpyableMock::concat.stub(|each| {
each.call(matching!("a", "b")).unmocked();
}))
.concat("a".to_string(), "b".to_string())
);
}
#[test]
#[should_panic(
expected = "Spyable::concat: Expected Spyable::concat(\"\", \"\") at tests/it/unmock.rs:73 to match at least 1 call, but it actually matched no calls."
)]
fn unmatched_pattern_still_panics() {
Unimock::new(SpyableMock::concat.stub(|each| {
each.call(matching!("", ""))
.returns("foobar")
.at_least_times(1);
each.call(matching!(_, _)).unmocked();
}))
.concat("a".to_string(), "b".to_string());
}
}
#[test]
fn unmock_recursion() {
#[unimock(api=FactorialMock, unmock_with=[my_factorial])]
trait Factorial {
fn factorial(&self, input: u32) -> u32;
}
fn my_factorial(f: &impl Factorial, input: u32) -> u32 {
f.factorial(input - 1) * input
}
assert_eq!(
120,
Unimock::new(FactorialMock::factorial.stub(|each| {
each.call(matching!(0 | 1)).returns(1u32);
each.call(matching!(_)).unmocked();
}))
.factorial(5)
);
}
#[tokio::test]
async fn unmock_async() {
#[unimock(api=AsyncFactorialMock, unmock_with=[my_factorial])]
#[async_trait]
trait AsyncFactorial {
async fn factorial(&self, input: u32) -> u32;
}
async fn my_factorial(f: &impl AsyncFactorial, input: u32) -> u32 {
f.factorial(input - 1).await * input
}
assert_eq!(
120,
Unimock::new_partial(
AsyncFactorialMock::factorial
.each_call(matching!(1))
.returns(1_u32)
)
.factorial(5)
.await,
"works using spy"
);
assert_eq!(
120,
Unimock::new(AsyncFactorialMock::factorial.stub(|each| {
each.call(matching!((input) if *input <= 1)).returns(1_u32);
each.call(matching!(_)).unmocked();
}))
.factorial(5)
.await,
"works using mock"
);
}
mod unmock_with_custom_args {
use super::*;
#[unimock(unmock_with=[foo(b, a)])]
trait FlippedOrder {
fn foo(&self, a: u8, b: u16) -> u32;
}
fn foo(b: u16, a: u8) -> u32 {
u32::from(b) + u32::from(a)
}
}
mod unmock_without_api_should_not_have_shadowing_problems_wrt_unmock_fns {
use super::*;
#[unimock(unmock_with=[foo])]
trait Spyable {
fn foo(&self);
}
fn foo(_: &impl std::any::Any) {}
}