深入了解模拟和存根:提高单元测试质量的关键技术

一、引言

在进行单元测试时,我们经常会遇到对外部资源的依赖,如数据库、网络接口等。模拟(Mocking)和存根(Stubbing)是两种帮助我们模拟这些外部资源,使我们能够在隔离环境中测试单元的方法。在本文中,我们将详细介绍模拟和存根,以及如何在单元测试中使用它们。

二、模拟和存根是什么?

模拟和存根都是用来模拟外部资源的技术,但它们的关注点略有不同:

  1. 存根(Stubs):存根是用来提供预设行为的。它们在你调用方法时返回一个特定的值或抛出一个异常。

  2. 模拟(Mocks):模拟不仅提供预设行为,还能检查调用的次数、参数等。通常我们用模拟来验证一个对象是否正确地使用了另一个对象。

三、使用模拟和存根的例子

下面,我们将使用JavaScript中的流行测试库,Jest,来展示如何使用模拟和存根。

例1:使用存根

假设你有一个函数getUserName,它从数据库中获取用户的名字。你想要测试这个函数,但是不想依赖真实的数据库。

  1. async function getUserName(userId) {

  2. const user = await db.getUser(userId);

  3. return user.name;

  4. }

你可以使用Jest的jest.fn()方法来创建一个存根:

  1. const db = {

  2. getUser: jest.fn()

  3. };

  4. db.getUser.mockResolvedValue({ name: 'Alice' });

  5. expect(await getUserName(1)).toBe('Alice');

在这个例子中,我们创建了一个存根来代替db.getUser方法。这个存根总是返回一个具有名字"Alice"的用户。

例2:使用模拟

假设你有一个login函数,它调用了一个authentication服务来验证用户的凭据。你想要测试这个函数,但是你更关心它是否正确地调用了authentication服务。

  1. async function login(username, password) {

  2. return await authentication.authenticate(username, password);

  3. }

你可以使用Jest的jest.fn()方法来创建一个模拟:

  1. const authentication = {

  2. authenticate: jest.fn()

  3. };

  4. authentication.authenticate.mockResolvedValue(true);

  5. await login('Alice', 'password');

  6. expect(authentication.authenticate).toHaveBeenCalledWith('Alice', 'password');

在这个例子中,我们创建了一个模拟来代替authentication.authenticate方法。然后,我们使用toHaveBeenCalledWith方法来验证login函数是否正确地调用了authentication.authenticate方法。

四、结论

模拟和存根是单元测试中的重要工具。它们帮助我们创建隔离的测试环境,使我们能够集中测试代码的特定部分,而不必担心其他外部依赖项。

下面是一些更深入使用模拟和存根的示例:

例3:更复杂的存根行为

在有些情况下,你可能想要模拟一些更复杂的行为,例如抛出异常。你可以通过配置存根来实现这一点。

  1. const db = {

  2. getUser: jest.fn()

  3. };

  4. db.getUser.mockRejectedValue(new Error('User not found'));

  5. try {

  6. await getUserName(1);

  7. } catch (e) {

  8. expect(e.message).toBe('User not found');

  9. }

在这个例子中,我们配置存根在被调用时抛出一个错误。然后,我们在测试中捕获这个错误,并验证它的消息是否正确。

例4:模拟函数调用的次数

除了验证模拟函数是否被正确的参数调用,你还可以验证模拟函数被调用的次数。

  1. const authentication = {

  2. authenticate: jest.fn()

  3. };

  4. authentication.authenticate.mockResolvedValue(true);

  5. await login('Alice', 'password');

  6. await login('Bob', 'password');

  7. expect(authentication.authenticate).toHaveBeenCalledTimes(2);

在这个例子中,我们使用toHaveBeenCalledTimes方法来验证login函数是否调用了两次authentication.authenticate方法。

五、结语

模拟和存根是一种强大的工具,可以帮助你编写出更加健壮和可维护的单元测试。通过了解何时以及如何使用这些工具,你可以更有效地测试你的代码,而无需担心外部依赖的复杂性。希望以上的示例可以帮助你在实践中更好地理解和应用模拟和存根。

六、参考链接

  • Jest: https://jestjs.io/

  • Sinon (另一款支持存根和模拟的JavaScript测试库): http://sinonjs.org/

行动吧,在路上总比一直观望的要好,未来的你肯定会感 谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入扣群: 320231853,里面有各种软件测试+开发资料和技术可以一起交流学习哦。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/685648.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【已解决】QT C++中QLineEdit不可粘贴输入

本博文源于生产实际,主要解决LineEdit不可粘贴输入的情况。下面将进行具体分析 问题来源 输入框只能一个个输入,不可复制粘贴。 分析 给QLineEdit装一个监听事件,监听它的事件即可。 问题解决步骤 问题一共分为三步: 书写监…

P8802 [蓝桥杯 2022 国 B] 出差

P8802 [蓝桥杯 2022 国 B] 出差 分析 很明显:单源最短路径 没有负权边 dijkstra 1.存图 2.准备两个数组 dis[]:更新源点到各个点的距离 vis[]:标记是否访问 3.从源点开始,更新源点到与其邻接的点的距离,每次选…

Minio(官方docker版)容器部署时区问题研究记录

文章目录 感慨&概述补充:MINIO_REGION和容器时间的关系 问题一:minio容器和本地容器时间不一致问题说明原因探究解决方法结果验证 问题二:minio修改时间和本地查询结果不一致具体问题原因探究解决办法时间转化工具类调用测试和验证上传文…

etcd单机部署和集群部署

1、etcd单实例部署 对于平常的学习,其实搭建一个单机节点是够了的。接下来就讲讲怎么搭建单机节点。 本次部署是在 centos7 系统,cpu 为amd64 上面进行的。 部署是直接使用官方编译好的二进制文件,大家也可以直接看 ectd-releases 界面选择…

有什么方便实用的黏土特效教程?6个软件教你快速进行特效制作

有什么方便实用的黏土特效教程?6个软件教你快速进行特效制作 作为时尚小达人,你自己是否想要制作出属于自己的黏土特效照片呢?比如下面几种。 看到这些黏土特效软件有没有心动,下面我也为大家详细的介绍一下可以制作出对应特效的…

邻域注意力Transformer

邻域注意力(NA),这是第一个高效且可扩展的视觉滑动窗口注意力机制,NA是一种逐像素操作,将自注意力(SA)定位到最近的相邻像素,因此与SA的二次复杂度相比,具有线性时间和空…

数据库被攻击后出现1044 - access denied for user ‘root‘@‘% ‘ to database table

MySQL数据库被攻击后,数据库全部被删除,并且加一个一个勒索的数据,向我索要btc, 出现这个问题就是我的数据库密码太简单了,弱密码,被破解了,并且把我权限也给修改了 导致我操作数据库时&#…

AI英语口语训练软件有哪些?这4款简单易用

AI英语口语训练软件有哪些?AI英语口语训练软件在现代语言学习中确实扮演着举足轻重的角色。它们凭借先进的智能算法,能够为我们量身定制学习方案,精准识别并纠正发音错误,模拟真实对话场景,使学习过程更加高效、有趣。…

【动态规划】子序列问题

最长上升子序列 题目描述: 解题思路: 核心思路: 用f[i]:表示以第i个数结尾的最大子序列,只需要找到比第i个小的最大子序列再加上1 即可; ----> f[i]max(f[j]1,f[i]); 定义 f[i] 表示以第 i 个元素结尾的最长上升子…

代码随想录算法训练营第36期DAY18

DAY18 二叉树的层序遍历 102二叉树的层序遍历 “队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。” 二叉树层序遍历模版: /** * Definition for a binary tree node. * struct TreeNode { *…

基于torch_dispatch机制生成Megatron-DeepSpeed调用关系图

基于torch_dispatch机制生成Megatron-DeepSpeed调用关系图 一.局部效果图二.运行训练过程,拦截算子,生成调用关系信息三.可视化,生成SVG图像 想知道Megatron-DeepSpeed训练过程中各模块之间的调用关系。torch_dispatch机制可以拦截算子,inspect又能获取到调用栈(文件,类名,函数…

《引爆流量获客技术》实操方法,手把手教你搭建盈利流量池

[1]-先导课.mp4 [2]-第1节:设计客户终身价值的方法和买客户思维.mp4 [3]-第2节:【渠道模型】解决谁是我的客户如何找到.mp4 [4]-第3节:【诱饵模型】解决 如何获得更多的客户.mp4 [5]-第4节:【钩子模型】解决让目标客户主动找你…