Rust Mock 工具

news2025/6/4 18:12:28

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.coloradmin.cn/o/2396423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Electron-vite【实战】MD 编辑器 -- 文件列表(含右键快捷菜单,重命名文件,删除本地文件,打开本地目录等)

最终效果 页面 src/renderer/src/App.vue <div class"dirPanel"><div class"panelTitle">文件列表</div><div class"searchFileBox"><Icon class"searchFileInputIcon" icon"material-symbols-light:…

华为云Flexus+DeepSeek征文|华为云Flexus云服务器X实例上部署Dify:打造高效的开源大语言模型应用开发平台

目录 前言 1 Dify与华为云部署概述 1.1 什么是 Dify 1.2 华为云与 Flexus 云服务器的优势 2 云服务器部署 Dify 的步骤详解 2.1 模板选择 2.2 参数配置 2.3 资源栈设置 2.4 确认部署信息并执行 3 部署成功后的操作与平台使用指南 3.1 访问平台 3.2 设置管理员账号 …

高密爆炸警钟长鸣:AI为化工安全戴上“智能护盾”

一、高密爆炸&#xff1a;一声巨响&#xff0c;撕开化工安全“伤疤” 2025年5月27日&#xff0c;山东高密友道化学有限公司的车间爆炸声&#xff0c;像一把利刃划破了化工行业的平静。剧烈的冲击波将车间夷为平地&#xff0c;黑色蘑菇云腾空而起&#xff0c;刺鼻的化学气味弥漫…

机器人学基础——正运动学(理论推导及c++实现)

机器人正运动学 机器人正运动学一般是指从机器人的关节位置到基于参考坐标系下末端执行器的位置。 平移变换和旋转变换 平移变换 假设我们有两个坐标系A和B&#xff0c;坐标系A与B的方位相同&#xff0c;xyz轴的指向都是一致的&#xff0c;即没有旋转变换。有一点p&#xf…

[网页五子棋][对战模块]处理连接成功,通知玩家就绪,逻辑问题(线程安全,先手判定错误)

文章目录 处理连接成功通知玩家就绪逻辑图问题 1&#xff1a;线程安全问题 2&#xff1a;先手判定错误两边都是提示&#xff1a;轮到对方落子![image.png](https://i-blog.csdnimg.cn/img_convert/c570cd26eadbe87ed467bc4edaa7945e.png) 处理连接成功 实现 GameAPI 的 afterC…

【Web应用】若依框架:基础篇11功能详解-系统接口

文章目录 ⭐前言⭐一、课程讲解⭐二、自己动手实操⭐总结 标题详情作者JosieBook头衔CSDN博客专家资格、阿里云社区专家博主、软件设计工程师博客内容开源、框架、软件工程、全栈&#xff08;,NET/Java/Python/C&#xff09;、数据库、操作系统、大数据、人工智能、工控、网络、…

【Docker项目实战篇】Docker部署PDF查看器PdfDing

【Docker项目实战篇】Docker部署PDD查看器PdfDing 一、PdfDing介绍1.1 PdfDing简介1.2 PdfDing主要特点1.3 主要使用场景 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Pd…

【Linux系统】第八节—进程概念(上)—冯诺依曼体系结构+操作系统+进程及进程状态+僵尸进程—详解!

hi&#xff0c;我是云边有个稻草人 偶尔中二的博主^(*&#xffe3;(oo)&#xffe3;)^&#xff0c;与你分享专业知识&#xff0c;祝博主们端午节快乐&#xff01; Linux—本节博客所属专栏—持续更新中—欢迎订阅&#xff01; 目录 一、冯诺依曼体系结构 二、操作系统(Opera…

数据结构——优先级队列(PriorityQueue)

1.优先级队列 优先级队列可以看作队列的另一个版本&#xff0c;队列的返回元素是由是由插入顺序决定的&#xff0c;先进先出嘛&#xff0c;但是有时我们可能想要返回优先级较高的元素&#xff0c;比如最大值&#xff1f;这种场景下就由优先级队列登场。 优先级队列底层是由堆实…

代谢组数据分析(二十六):LC-MS/MS代谢组学和脂质组学数据的分析流程

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包依赖包安装包加载需要的R包数据下载以及转换mzML数据预处理代谢物注释LipidFinder过滤MultiABLER数据预处理过滤补缺失值对数变换数据标准化下游数据分析总结系统信息参考介…

【ARM】【FPGA】【硬件开发】Chapter.1 AXI4总线协议

Chapter.1 AXI4总线协议 作者&#xff1a;齐花Guyc(CAUC) 一、总线介绍 AXI4总线 AXI4总线就像是SoC内部的“高速公路”&#xff0c;负责在不同硬件模块之间高效传输数据。 AXI4协议通过 5个独立通道 传输数据和控制信号&#xff0c;每个通道都有自己的信号线&#xff0c;互…

把 jar 打包成 exe

1. 把自己的项目先正常打成jar包 2. 使用exe4j工具将jar转换为exe 2.1 exe4j下载地址&#xff1a;https://www.ej-technologies.com/download/exe4j/files 2.2 下载完成之后激活 2.3 可以点击Change License&#xff0c;输入秘钥L-g782dn2d-1f1yqxx1rv1sqd 2.4 直接下一步…

【目标检测】检测网络中neck的核心作用

1. neck最主要的作用就是特征融合&#xff0c;融合就是将具有不同大小感受野的特征图进行了耦合&#xff0c;从而增强了特征图的表达能力。 2. neck决定了head的数量&#xff0c;进而潜在决定了不同尺度样本如何分配到不同的head&#xff0c;这一点可以看做是将整个网络的多尺…

【经验】Ubuntu中设置terminator的滚动行数、从Virtualbox复制到Windows时每行后多一空行

1、设置terminator的滚动行数 1.1 问题描述 在终端 terminator 中&#xff0c;调试程序时&#xff0c;只能查看有限行数的打印日志&#xff0c;大约是500行&#xff0c;怎么能增加行数 1.2 解决方法 1&#xff09;安装terminator sudo apt install terminator和 terminato…

使用微软最近开源的WSL在Windows上优雅的运行Linux

install wsl https://github.com/microsoft/WSL/releases/download/2.4.13/wsl.2.4.13.0.x64.msi install any distribution from microsoft store, such as kali-linux from Kali office website list of distribution PS C:\Users\50240> wsl -l -o 以下是可安装的有…

HackMyVM-Teacher

信息搜集 主机发现 ┌──(kali㉿kali)-[~] └─$ nmap -sn 192.168.43.0/24 Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-01 01:02 EDT Nmap scan report for 192.168.43.1 Host is up (0.0084s latency). MAC Address: C6:45:66:05:91:88 (Unknow…

BugKu Web渗透之矛盾

开启场景&#xff0c;打开网页。发现是一段php代码。 这段代码也很好理解&#xff0c;就是get方式传参num&#xff0c;如果num不是数字类型&#xff0c;那么输出num的值&#xff0c;并且num1时&#xff0c;输出flag的值。 首先看看is_numeric的意思。 开始我想到了使用科学技术…

Python实现P-PSO优化算法优化卷积神经网络CNN回归模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 随着人工智能和深度学习技术的快速发展&#xff0c;卷积神经网络&#xff08;CNN&#xff09;在图像分类、目标检测…

ssm 学习笔记day03

环境搭建 spring配置数据库 1.在pom.xml安装相应的依赖 2.在properties里面配置数据库的相关信息&#xff0c;需要强调的一点是&#xff0c;一定不要在properties里面添加任何空格&#xff0c;否则就会像我一样搞了两小时&#xff0c;数据一直报错&#xff0c;然后发现是空格的…

mkdir: cannot create directory ‘gitlab-stu’: No space left on device

Linux中创建目录时报错“mkdir: cannot create directory ‘gitlab-stu’: No space left on device”&#xff0c;磁盘空间不足。 使用df命令查看&#xff0c;发现 / 下面use%占满了&#xff1a; 查看inode使用情况&#xff1a; 可以看到docker的数据大部分存放在/var/lib/do…