Rust 用 mockall 做 Mock 测试:给 trait 接口写假实现并验证调用
test12026-10-210 次阅读
为什么需要 mock
业务代码依赖发邮件、访问数据库、调第三方服务时,单测不该真去连。我们会把这些依赖抽象成 trait,测试时塞一个“假实现”进去。手写假实现很啰嗦,mockall 能根据 trait 自动生成可配置的假对象,并且能断言“有没有被调用、用什么参数、调用几次”。
定义被依赖的 trait
use mockall::{mock, predicate::*};
pub trait Mailer {
fn send(&self, to: &str, body: &str) -> Result<(), String>;
}
pub struct Notifier<'a> {
mailer: &'a dyn Mailer,
}
impl<'a> Notifier<'a> {
pub fn welcome(&self, email: &str) -> Result<(), String> {
self.mailer.send(email, "Welcome!")
}
}用 mock! 生成假实现并验证
expect_send 设定期望:参数要等于某值、调用次数为 1、返回 Ok。测试结束后,mockall 会自动检查期望是否满足,没满足就报错。
mock! {
MailerMock {}
impl Mailer for MailerMock {
fn send(&self, to: &str, body: &str) -> Result<(), String>;
}
}
#[test]
fn sends_welcome_email() {
let mut mock = MailerMock::new();
mock.expect_send()
.with(eq("a@b.com"), eq("Welcome!"))
.times(1)
.returning(|_, _| Ok(()));
let notifier = Notifier { mailer: &mock };
assert!(notifier.welcome("a@b.com").is_ok());
}验证错误分支
换个测试,让 mock 返回 Err,断言业务方把错误正确透传。这样成功与失败两条链路都能覆盖,且不依赖真实邮件服务。
#[test]
fn surfaces_send_failure() {
let mut mock = MailerMock::new();
mock.expect_send()
.returning(|_, _| Err("smtp down".into()));
let notifier = Notifier { mailer: &mock };
assert_eq!(notifier.welcome("a@b.com"), Err("smtp down".into()));
}配合 thiserror 定义库错误、anyhow 收口应用层错误时,mock 的返回值直接是 Result,测试写起来和线上代码一致,没有额外心智负担。
T
test1
文章作者
为什么需要 mock 业务代码依赖发邮件、访问数据库、调第三方服务时,单测不该真去连。我们会把这些依赖抽象成 trait...
- 分类
- 技术
- 发布时间
- 2026-10-21
- 字数
- 约 1484 字
- 阅读
- 0 次
