在单元测试中使用Jest模拟VS Code extension API

对VS Code extension进行单元测试时通常会遇到一个问题,代码中所使用的VS Code编辑器的功能都依赖于vscode库,但是我们在单元测试中并没有添加对vscode库的依赖,所以导致运行单元测试时出错。由于vscode库是作为第三方依赖被引入到我们的VS Code extension中的,所以它并不受我们的控制,最好的办法就是在单元测试中对其中的API进行模拟。本文中我将介绍如何使用Jest来模拟vscode库的API。

  如果你还不太熟悉如何开始创建一个VS Code extension,这里的文档可以教你快速上手。

  创建好VS Code extension项目后,你会发现在根目录下有一个package.json文件,VS Code extension会从中读取配置项来管理UI界面元素,在实际开发中你可能会使用到其中的一些属性。我们可以通过package.json来设置项目所需要的依赖项,这里我们将Jest添加为dev dependency,并添加npm脚本以运行Jest单元测试。

npm i -D jest
{"scripts": {"test": "jest"}
}

模拟VS Code node module

  Jest提供了一些mocking的选项,但是因为我们想要模拟整个vscode node module,所以最简单的办法是在与node_modules文件夹相同的位置(通常是项目的根目录)创建一个__mocks__文件夹,并在其中添加一个与要模拟的模块名称相同的文件(vscode.js)。

  你不需要在测试代码中导入该模块,mock会自动加载它。Jest称此为manual mocks。

  这种方法最大的好处是它能将我们的测试代码与所依赖的模块分离,使测试代码看起来更加整洁。这里有一个小问题,新加入的开发者需要知道__mocks__文件夹,否则很难理解单元测试是如何正常工作的,因为单元测试中并没有VS Code模块被模拟的代码。

  以下就是对VS Code模块进行模拟的代码。我们并没有模拟整个API,你可以根据需要进行调整。

// vscode.jsconst languages = {createDiagnosticCollection: jest.fn()
};const StatusBarAlignment = {};const window = {createStatusBarItem: jest.fn(() => ({show: jest.fn()})),showErrorMessage: jest.fn(),showWarningMessage: jest.fn(),createTextEditorDecorationType: jest.fn()
};const workspace = {getConfiguration: jest.fn(),workspaceFolders: [],onDidSaveTextDocument: jest.fn()
};const OverviewRulerLane = {Left: null
};const Uri = {file: f => f,parse: jest.fn()
};
const Range = jest.fn();
const Diagnostic = jest.fn();
const DiagnosticSeverity = { Error: 0, Warning: 1, Information: 2, Hint: 3 };const debug = {onDidTerminateDebugSession: jest.fn(),startDebugging: jest.fn()
};const commands = {executeCommand: jest.fn()
};const vscode = {languages,StatusBarAlignment,window,workspace,OverviewRulerLane,Uri,Range,Diagnostic,DiagnosticSeverity,debug,commands
};module.exports = vscode;

使用模拟的VS Code模块的示例

  我的开源项目Git Mob for VS code中使用了这种方法,我将用其中的代码来说明如何使用模拟的VS Code模块。

  下面的例子中,VS Code编辑器的状态栏会根据Git钩子prepare-commit-msg是否被调用来做相应的调整,你可以看到这里我并没有将vscode模块导入到我的测试文件中并对其进行模拟。

// git-mob-hook-status.spec.jsconst { hasPrepareCommitMsgTemplate } = require("../prepare-commit-msg-file");
const { gitMobHookStatus } = require("./git-mob-hook-status");jest.mock("./../prepare-commit-msg-file");describe("Hook or template status", function() {let mockContext;beforeAll(function() {mockContext = {subscriptions: []};});afterEach(function() {hasPrepareCommitMsgTemplate.mockReset();});it("using git template for co-authors", () => {hasPrepareCommitMsgTemplate.mockReturnValue(false);const statusBar = gitMobHookStatus({ context: mockContext })();expect(statusBar).toEqual(expect.objectContaining({text: "$(file-code) Git Mob",tooltip: "Using .gitmessage template"}));});it("using git prepare commit msg for co-authors", () => {hasPrepareCommitMsgTemplate.mockReturnValue(true);const statusBar = gitMobHookStatus({ context: mockContext })();expect(statusBar).toEqual(expect.objectContaining({text: "$(zap) Git Mob",tooltip: "Using prepare-commit-msg hook"}));});
});

// git-mob-hook-status.js
const vscode = require("vscode");
const { hasPrepareCommitMsgTemplate } = require("../prepare-commit-msg-file");function gitMobHookStatus({ context }) {const myStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left,10);context.subscriptions.push(myStatusBarItem);return function() {myStatusBarItem.text = "$(file-code) Git Mob";myStatusBarItem.tooltip = "Using .gitmessage template";if (hasPrepareCommitMsgTemplate()) {myStatusBarItem.text = "$(zap) Git Mob";myStatusBarItem.tooltip = "Using prepare-commit-msg hook";}myStatusBarItem.show();return myStatusBarItem;};
}exports.gitMobHookStatus = gitMobHookStatus;

  你可以在这里查看源代码:

  • git-mob-hook-status.spec.js
  • git-mob-hook-status.js

我能检查vscode模块中的方法是否被调用了吗?

  你可以导入模拟的vscode模块。下面的代码中,我想要检查当用户修改co-author文件时onDidSaveTextDocument事件是否被订阅了。

const vscode = require("../__mocks__/vscode");// ...
test("Reload co-author list when git-coauthors file saved", () => {reloadOnSave(coAuthorProviderStub);expect(vscode.workspace.onDidSaveTextDocument).toHaveBeenCalledWith(expect.any(Function));// ...
});
// ...

  可以看到这里都是Jest mock API的标准用法,这意味着我们可以在代码中正常使用vscode模块的方法,而不受manual mock的任何限制。例如,我们还可以使用mockImplementation来修改实现。

  更多示例可以查看这里的源代码:

  • reload-on-save.spec.js

  编写单元测试最大的好处是可以快速得到反馈结果,如果你对TDD(Test-Driven Development,测试驱动开发)情有独钟,那么单元测试将使你对VS Code extension的开发更加信心满满。


 资料获取方法

【留言777】

各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!

三连之后我会在评论区挨个私信发给你们~

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

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

相关文章

Elasticsearch 与 OpenSearch:揭开性能差距

作者:George Kobar, Ugo Sangiorgi 对于任何依赖快速、准确搜索数据的组织来说,强大、快速且高效的搜索引擎是至关重要的元素。 对于开发人员和架构师来说,选择正确的搜索平台可以极大地影响你的组织提供快速且相关结果的能力。 在我们全面的…

学习笔记-JVM-工具包(JVM分析工具)

常用工具 JDK工具 ① jps: JVM Process status tool:JVM进程状态工具,查看进程基本信息 ② jstat: JVM statistics monitoring tool : JVM统计监控工具,查看堆,GC详细信息 ③ jinfo:Java Configuration I…

数据结构之红黑树

二叉搜索树对于某个节点而言,其左子树的节点关键值都小于该节点关键值,右子树的所有节点关键值都大于该节点关键值。二叉搜索树作为一种数据结构,其查找、插入和删除操作的时间复杂度都为O(logn),底数为2。但是我们说这个时间复杂度是在平衡的…

STM32 F103C8T6学习笔记4:时钟树、滴答计时器、定时器定时中断

今日理解一下STM32F103 C8T6的时钟与时钟系统、滴答计时器、定时器计时中断的配置,文章提供原理,代码,测试工程下载。 目录 时钟树与时钟系统: 滴答计时器: 定时器计时中断: 测试结果: 测…

【RabbitMQ与SpringBoot集成测试收发消息】

【RabbitMQ与SpringBoot集成测试收发消息】 一、环境说明二、实验步骤三、小结 一、环境说明 安装环境:虚拟机VMWare Centos7.6 Maven3.6.3 JDK1.8RabbitMQ版本:rabbitmq-server-3.8.8-1.el7.noarch.rpm编程工具Idea 运行JDK为17 二、实验步骤 在Rab…

微服务 云原生:基于 Gogs + Drone 实现 CI/CD 自动化

一般构建部署 以一个简单的前后端项目来说,分别编写前后端的 Dockerfile 文件并构建镜像,然后编写 docker-compose.yml 构建部署,启动运行。每次代码变更后都需重新手动打包、构建、推送。 一个简单的例子: 前端: 项…

数据库的存储过程、触发器、事件 实现(超精简)

一 存储过程 什么是存储过程 : 自己搜 和代码写的有什么区别: 没区别 为什么用存储过程: 快 例子 -- 创建 test名字的存储过程 CREATE PROCEDURE test(in idin INT) BEGIN-- 创建变量declare id int default 0;declare stopflag int defau…

tomcat入门介绍

tomcat官网下载8.5.9版本,官网地址:https://tomcat.apache.org/download-80.cgi 下载完成后直接解压即可 tomcat目录 解压后,可以看到tomcat有以下目录 /bin - 启动、关闭和其他脚本 *.sh后缀是linux下的脚本文件*.bat后缀windows系统下的…

【C语言学习】函数的定义和调用

一、函数定义 要有返回类型、函数名和函数体 二、调用函数 函数名(函数值); ()起到表示函数调用的重要作用,即使没有参数也需要() 若有参数,则需要给出正确的数量和顺序…

内网隧道—HTTP\DNS\ICMP

本文仅限于安全研究和学习,用户承担因使用此工具而导致的所有法律和相关责任! 作者不承担任何法律和相关责任! HTTP隧道 Neo-reGeorg Neo-reGeorg 是一个旨在积极重构 reGeorg 的项目,目的是: 提高可用性&#xff0…

innovus 报告多边形floorplan的boundary坐标

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球 历史文章: 常用dbGet命令 dbGet快速入门 使用"Cut Rectilinear"功能可以​调整floorplan形状,使其变成非矩形多边形(polygon)。​ …

前端面试自我介绍

前端面试自我介绍精选篇1 各位面试官大家好,我叫__,就读于__大学__学,大学本科学历,我的求职意向是与金融专业相关的职位,本人拥有较强的学习能力,能快速适应工作环境,兴趣爱好广泛&#xff0c…