9 个版本 (5 个破坏性更新)
使用旧的Rust 2015
0.8.0 | 2020年10月17日 |
---|---|
0.7.0 | 2017年11月17日 |
0.6.1 | 2017年9月13日 |
0.5.0 | 2017年7月19日 |
0.3.1 | 2017年7月16日 |
在 过程宏 中排名 1682
每月下载量 44
41KB
767 行代码(不包括注释)
Mock_Derive 是一个易于设置、功能丰富的Rust编程语言模拟库。当你与另一个测试系统(如 cargo test
)结合使用时,它可以让你快速设置单元测试。
为了安装,只需将以下行添加到你的Cargo.toml中
[dependencies]
mock_derive = "0.8.0"
请注意,mock_derive 不是一个1.0版本的crate,仍在积极开发中。因此,你可能发现一些尚未支持的实际用例。如果你发现这样的用例,请提交一个issue,我们将尽快处理。
mock_dervice 于2017年开发,但由于主要贡献者的生活情况,开发暂停。2020年,开发重新开始。
Mock_Derive 与其他语言中的模拟库有何不同。
在传统的OO语言中,模拟通常基于继承,或者在更动态的语言中混合方法替换。你从一个模拟工厂创建一个 Foo
,定义那个 Foo
的行为,并将其传递给期望一个 Foo
的函数。Rust没有传统的继承,这意味着“只有Foo是Foo”。Mock_Derive 鼓励实现模拟。这意味着你将为一个特质推导出你的模拟。你将把这个模拟传递给期望实现那个特质的函数,并且你将能够控制该模拟的行为,类似于你过去可能使用过的其他模拟库。
示例
查看src/examples以获取工作示例
使用此crate看起来像这样
#![feature(proc_macro)]
extern crate mock_derive;
use mock_derive::mock;
#[mock]
pub trait CustomTrait {
fn get_int(&self) -> u32;
fn opt_int(&self) -> Option<u32>;
fn default_method(&self, x: i32, y: i32) -> i32 {
x + y
}
}
您会注意到,在我们的特质定义上方包含了一个#[mock]指令。默认情况下,这将生成一个名为"MockCustomTrait"的CustomTrait实现,它包含用于控制其行为的辅助函数。例如,我们可以编写以下测试函数:
#[test]
fn it_works() {
let foo = Foo::new(); // Foo here is a struct that implements CustomTrait
let mut mock = MockCustomTrait::new();
mock.set_fallback(foo); // If a behavior isn't specified, we will fall back to this object's behavior.
let method = mock.method_get_int()
.first_call()
.set_result(3)
.second_call()
.set_result(4)
.nth_call(3) // This is saying 'third_call'
.set_result(5);
mock.set_get_int(method); // Due to Rust's ownership model, we will need to set our mock method
// on our mock
let result = mock.get_int();
assert!(result == 3);
let result2 = mock.get_int();
assert!(result2 == 4);
let result3 = mock.get_int();
assert!(result3 == 5);
// This is a fallback case
let result4 = mock.get_int();
assert!(result4 == 1);
}
// You can also pass in a lambda to return a value. This can be used to return a value
// an infinite number of times, or mutate state to simulate an object across calls.
#[test]
fn return_result_of() {
let mut x = 15;
let mut mock = MockCustomTrait::new();
let method = mock.method_opt_int()
.return_result_of(move || {
x += 1;
Some(x)
});
mock.set_opt_int(method);
assert!(mock.opt_int() == Some(16));
assert!(mock.opt_int() == Some(17));
assert!(mock.opt_int() == Some(18));
}
// You can also specify the total number of calls (i.e. once, exactly 5 times, at least 5 times, at most 10 times, etc.)
#[test]
// When using "should panic" it's suggested you look for specific errors
#[should_panic(expected = "called at least")]
fn min_calls_not_met() {
let mut mock = MockCustomTrait::new();
let method = mock.method_get_int()
.called_at_least(10)
.return_result_of(|| 10);
mock.set_foo(method);
for _ in 0..9 {
mock.get_int();
}
}
#[test]
fn called_once() {
let mut mock = MockCustomTrait::new();
let method = mock.method_get_int()
.called_once()
.return_result_of(|| 10);
mock.set_foo(method);
mock.get_int(); // Commenting this line out would trigger a failure
// mock.get_int(); // This would trigger a failure
}
外部函数
截至mock_derive 0.6.1,您现在可以模拟静态外部函数。它们与特质模拟共享相同的API。请查看tests/src/foriegn_functions.rs以获取更多示例。
use mock_derive::mock;
// In #[cfg(test)], this will generate functions named 'c_double', 'c_div', etc that you can control
// the behavior of. When not in #[cfg(test)], #[mock] is a noop, meaning that no overhead is added,
// and your program behaves as normal.
#[mock]
extern "C" {
pub fn c_double(x: isize) -> isize;
pub fn c_div(x: isize, y: isize) -> isize;
fn side_effect_fn(x: usize, y: usize);
fn no_args_no_ret();
}
#[mock]
extern "Rust" {
fn x_double(x: isize) -> isize;
}
#[test]
fn extern_c_test() {
let mock = ExternCMocks::method_c_double()
.first_call()
.set_result(2);
ExternCMocks::set_c_double(mock);
unsafe { assert!(c_double(1) == 2); }
}
#[test]
fn extern_rust_test() {
let mock = ExternRustMocks::method_x_double()
.first_call()
.set_result(2);
ExternRustMocks::set_x_double(mock);
unsafe { assert!(x_double(1) == 2) };
}
泛型
截至mock_derive 0.5.0,我们支持泛型(基本支持)。请查看tests/src/generics.rs以获取更多示例。
#[mock]
trait GenericTrait<T, U>
where T: Clone {
fn merge(&self, t: T, u: U) -> U;
}
#[test]
fn generic_test_one() {
let mut mock = MockGenericTrait::<f32, i32>::new();
let method = mock.method_merge()
.called_once()
.set_result(30);
mock.set_merge(method);
assert!(mock.merge(15.0, 15) == 30);
}
#[mock]
trait LifetimeTrait<'a, T>
where T: 'a {
fn return_value(&self, t: T) -> &'a T;
}
static TEST_FLOAT: f32 = 1.0;
#[test]
fn generics_and_lifetime() {
let mut mock = MockLifetimeTrait::<'static, f32>::new();
let method = mock.method_return_value()
.called_once()
.set_result(&TEST_FLOAT);
mock.set_return_value(method);
assert!(mock.return_value(TEST_FLOAT.clone()) == &TEST_FLOAT);
}
测试
在tests/目录中有些测试同时作为示例。进入该目录并运行cargo test
。
贡献
任何人都可以贡献!如果您有想要贡献的添加/错误修复,只需打开一个PR,它将会被审查。欢迎未完成的PR。只需在PR的名称中包含[WIP]。
许可证
Mock_Derive遵循MIT许可证。
依赖项
~1.5MB
~35K SLoC