当前位置: 首页 > article >正文

Rust Mock 工具

Rust Mock 工具

Mock(模拟)是测试中不可或缺的工具,用来替代复杂或不可控的依赖,比如数据库、网络服务等,帮助我们写出高质量、健壮的测试代码。Rust 社区中,mockallmockito 是两款主流且强大的 Mock 库,分别针对 trait 依赖和 HTTP 请求。


目录

  1. Mock 基础理解与意义

  2. mockall:Rust Trait Mock 实战

    • 2.1 安装与基本用法
    • 2.2 参数匹配器技巧
    • 2.3 调用次数与顺序控制
    • 2.4 返回值高级控制
    • 2.5 异步函数 Mock
    • 2.6 手写 Mock vs 自动生成 Mock
    • 2.7 常见坑与调试技巧
  3. mockito:HTTP 请求 Mock 实战

    • 3.1 安装与基础用法
    • 3.2 匹配请求头、请求体及路径参数
    • 3.3 模拟网络延迟与错误
    • 3.4 多个 Mock 交互与验证调用顺序

1. Mock 基础理解与意义

  • 为什么要用 Mock?
    测试真实依赖(数据库、网络)通常会慢、不稳定,且不易覆盖异常路径。Mock 允许我们模拟依赖行为,快速且精准地测试业务逻辑。

  • Mock 和 Stub 的区别?
    Stub 只是返回预设结果的替身,Mock 除了返回结果,还能验证调用是否发生、参数是否正确、调用顺序等。

  • Rust Mock 工具特点
    Rust 静态类型强且拥有 trait 机制,Mock 库多基于 trait 来实现替身行为,网络请求 Mock 则独立存在。


2. mockall:Trait Mock 实战

mockall 通过 #[automock] 宏自动生成 Mock 结构体,支持丰富的匹配和返回值设置。

2.1 安装与基础用法

Cargo.toml:

[dev-dependencies]
mockall = "0.11"

示例:

use mockall::{automock, predicate::*};#[automock]
pub trait UserRepo {fn find_user(&self, user_id: u32) -> Option<String>;
}#[cfg(test)]
mod tests {use super::*;#[test]fn basic_mock() {let mut mock = MockUserRepo::new();mock.expect_find_user().with(predicate::eq(42)).times(1).returning(|_| Some("Alice".to_string()));assert_eq!(mock.find_user(42), Some("Alice".to_string()));}
}

2.2 参数匹配器技巧

mockall 提供丰富参数匹配器 predicate,常用:

  • predicate::eq(value) - 等于
  • predicate::always() - 任意参数都匹配
  • predicate::lt(value), gt(), le(), ge() - 大小比较
  • 自定义闭包匹配 predicate::function(|arg| ...)

示例:

mock.expect_find_user().with(predicate::function(|id| *id > 0)).returning(|id| Some(format!("User{}", id)));

2.3 调用次数与顺序控制

  • .times(n) 指定调用次数
  • .once() 等价 .times(1)
  • .never() 期望不被调用
  • .in_sequence(&mut seq) 用于控制调用顺序(配合 Sequence 类型)

示例:

use mockall::Sequence;let mut seq = Sequence::new();mock.expect_find_user().with(predicate::eq(1)).times(1).in_sequence(&mut seq).returning(|_| Some("First".to_string()));mock.expect_find_user().with(predicate::eq(2)).times(1).in_sequence(&mut seq).returning(|_| Some("Second".to_string()));

这样会验证先调用参数1再调用参数2。


2.4 返回值高级控制

  • .returning() 支持闭包返回不同结果
  • .return_once() 返回一次后失效,之后走 .returning() 默认行为
  • .panic() 用于模拟异常

示例:

mock.expect_find_user().with(predicate::eq(1)).return_once(|_| Some("One".to_string())).return_once(|_| Some("Two".to_string())).returning(|_| None);

2.5 异步函数 Mock

支持 async trait:

use std::future::Future;
use std::pin::Pin;#[automock]
trait AsyncTrait {fn fetch(&self) -> Pin<Box<dyn Future<Output=String> + Send>>;
}#[tokio::test]
async fn async_mock_test() {let mut mock = MockAsyncTrait::new();mock.expect_fetch().returning(|| Box::pin(async { "async result".to_string() }));let res = mock.fetch().await;assert_eq!(res, "async result");
}

2.6 手写 Mock vs 自动生成 Mock

  • 自动生成方便快捷,推荐用 #[automock]
  • 手写 Mock 更灵活,适合复杂场景或无 trait 的情况

手写示例:

struct ManualMock;impl UserRepo for ManualMock {fn find_user(&self, user_id: u32) -> Option<String> {if user_id == 42 { Some("Manual".to_string()) } else { None }}
}

2.7 常见坑与调试技巧

  • times() 配置不当导致测试失败:检查调用次数是否和预期匹配
  • 调用顺序不对:用 Sequence 管理调用顺序
  • 参数匹配失败:用 .with(predicate::always()) 暂时放宽匹配,确认调用
  • 调试信息:Rust 测试输出可用 println! 配合 Mock 函数打印确认执行流程

3. mockito:HTTP 请求 Mock

3.1 安装

[dev-dependencies]
mockito = "0.31"

3.2 简单示例

#[cfg(test)]
mod tests {use mockito::mock;#[test]fn http_mock() {let _m = mock("GET", "/api/user/42").with_status(200).with_body(r#"{"name":"Alice"}"#).create();let url = format!("{}/api/user/42", mockito::server_url());let resp = reqwest::blocking::get(&url).unwrap();let body = resp.text().unwrap();assert_eq!(body, r#"{"name":"Alice"}"#);}
}

3.3 复杂匹配

  • 匹配请求头
mock("POST", "/submit").match_header("content-type", "application/json").with_status(201).create();
  • 匹配请求体
use mockito::Matcher;mock("POST", "/submit").match_body(Matcher::JsonString(r#"{"key":"value"}"#.to_string())).with_status(201).create();

3.4 模拟延时、失败

mock("GET", "/slow").with_status(200).with_body("delayed").with_delay(std::time::Duration::from_secs(3)).create();

模拟网络错误:

mock("GET", "/fail").with_status(500).with_body("Internal Server Error").create();

3.5 多个 Mock 与调用顺序

let _m1 = mock("GET", "/step1").with_status(200).with_body("first").create();let _m2 = mock("GET", "/step2").with_status(200).with_body("second").create();

请求 /step1/step2 会匹配对应 Mock。

http://www.lryc.cn/news/2396423.html

相关文章:

  • C++读写锁以及实现方式
  • Electron-vite【实战】MD 编辑器 -- 文件列表(含右键快捷菜单,重命名文件,删除本地文件,打开本地目录等)
  • 华为云Flexus+DeepSeek征文|华为云Flexus云服务器X实例上部署Dify:打造高效的开源大语言模型应用开发平台
  • [git每日一句]Your branch is up to date with ‘origin/master‘
  • 高密爆炸警钟长鸣:AI为化工安全戴上“智能护盾”
  • 机器人学基础——正运动学(理论推导及c++实现)
  • [网页五子棋][对战模块]处理连接成功,通知玩家就绪,逻辑问题(线程安全,先手判定错误)
  • TensorFlow Extended (TFX) 生产环境模型版本控制与回滚实战指南
  • 【Web应用】若依框架:基础篇11功能详解-系统接口
  • 【Docker项目实战篇】Docker部署PDF查看器PdfDing
  • Redis 常用数据类型和命令使用
  • 【Linux系统】第八节—进程概念(上)—冯诺依曼体系结构+操作系统+进程及进程状态+僵尸进程—详解!
  • WPF 全局加载界面、多界面实现渐变过渡效果
  • WebSocket与实时对话式AI服务的集成
  • 【xmb】】内部文档148344599
  • MobaXterm国内下载与安装使用教程
  • 数据结构——优先级队列(PriorityQueue)
  • 代谢组数据分析(二十六):LC-MS/MS代谢组学和脂质组学数据的分析流程
  • 服务器上用脚本跑python深度学习的注意事项(ubantu系统)
  • 【ARM】【FPGA】【硬件开发】Chapter.1 AXI4总线协议
  • 青少年编程与数学 02-020 C#程序设计基础 10课题、桌面应用开发
  • 把 jar 打包成 exe
  • 【目标检测】检测网络中neck的核心作用
  • 【经验】Ubuntu中设置terminator的滚动行数、从Virtualbox复制到Windows时每行后多一空行
  • 使用微软最近开源的WSL在Windows上优雅的运行Linux
  • HackMyVM-Teacher
  • BugKu Web渗透之矛盾
  • hot100 -- 4.子串系列
  • Python实现P-PSO优化算法优化卷积神经网络CNN回归模型项目实战
  • ssm 学习笔记day03