在单元测试中使用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.rhkb.cn/news/87041.html

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

相关文章

3.1 Spring MVC概述

1. MVC概念 MVC是一种编程思想,它将应用分为模型(Model)、视图(View)、控制器(Controller)三个层次,这三部分以最低的耦合进行协同工作,从而提高应用的可扩展性及可维护…

【Archaius技术专题】「Netflix原生态」动态化配置服务之微服务配置组件变色龙

前提介绍 如果要设计开发一套微服务基础架构,参数化配置是一个非常重要的点,而Netflix也开源了一个叫变色龙Archaius的配置中心客户端,而且Archaius可以说是比其他客户端具备更多生产级特性,也更灵活。*在NetflixOSS微服务技术栈…

资源限制类题目解法,看这一篇就够了!

算法拾遗三十七资源限制类题目 资源限制技巧汇总32位无符号整数的范围是0~4,294,967,295,现在有一个正好包含40亿个无符号整数的文件,可以使用最多1GB的内存,怎么找到出现次数最多的数32位无符号整数的范围是0~4294967295,现在又一…

人工智能讲师AIGC讲师叶梓:大模型这么火,我们在使用时应该关注些什么?-2

以下为叶老师讲义分享: P6-P9 一些考验大模型的经典问题: 1、鲁迅与周树人是同一个人吗?2、圆周率的最后一位3、蓝牙耳机坏了4、最新的:奶奶的睡前故事 关于事实的问答结果: 知识的时效性: 未完,下一章继续……

【Unity实战系列】Unity的下载安装以及汉化教程

君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们,这里是君兮_,怎么说呢,其实这才是我以后真正想写想做的东西,虽然才刚开始,但好歹,我总算是启程了。今天要分享…

数据库活动监控(DAM)

在当今数据驱动的世界中,组织在保护存储在数据库中的机密数据并确保其完整性方面面临着越来越多的挑战。数据库审计通过提供全面的数据库活动监控方法,在应对这些挑战方面发挥着至关重要的作用。 数据库活动监控(Database Activity Monitori…

2023河南萌新联赛第(五)场:郑州轻工业大学-F 布鲁特佛斯

2023河南萌新联赛第(五)场:郑州轻工业大学-F 布鲁特佛斯 https://ac.nowcoder.com/acm/contest/62977/F 文章目录 2023河南萌新联赛第(五)场:郑州轻工业大学-F 布鲁特佛斯题意解题思路代码 题意 给定一个…

SpringCloudGateway配置跨域设置以及如何本地测试跨域

问题背景 有个服务A ,自身对外提供服务,几个系统的前端页面也在调用,使用springboot 2.6.8开发的,自身因为有前端直接调用已经配置了跨域。 现在有网关服务,一部分前端通过网关访问服务A(因为之前没有网关…

预测知识 | 预测技术流程及模型评价

预测知识 | 预测技术流程及模型评价 目录 预测知识 | 预测技术流程及模型评价技术流程模型评价参考资料 技术流程 1)模型训练阶段:预测因素和结局,再加上预测模型进行模型拟合; 2)预测阶段:将预测因素代入拟…

大数据课程I2——Kafka的架构

文章作者邮箱:yugongshiyesina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握Kafka的架构; ⚪ 掌握Kafka的Topic与Partition; 一、Kafka核心概念及操作 1. producer生产者,可以是一个测试线程,也…

web-xss

目录 一、简介 二、xss的攻击方式 三、xss 常见标签语句 a标签 img标签 iframe标签 audio标签 video标签 svg标签 button标签 div标签 object标签 script标签 p标签 input标签 details标签 select标签 form标签 body标签 四、xss 常见绕过 编码绕过 1.htm…

(5)所有角色数据分析页面的构建-5

所有角色数据分析页面,包括一个时间轴柱状图、六个散点图、六个柱状图(每个属性角色的生命值/防御力/攻击力的max与min的对比)。 """绘图""" from pyecharts.charts import Timeline from find_type import FindType import pandas …

模仿火星科技 基于cesium+角度测量+高度测量+可编辑

1. 创建提示窗: 启动Cesium应用,地图场景将打开,欢迎您进入编辑模式。 在屏幕的一角,一个友好的提示窗将呈现,随着您的操作,它会为您提供有用的信息和指导。 2. 绘制面积: 轻轻点击鼠标左键&a…

iOS- git对单个或者多个文件权限设置,使用pre-commit hook 和shell脚本,拦截校验

前提:最近,由于团队代码规范和安全问题,有一些文件只能是指定用户才能修改。 对比:调查了一下资料,发现好多人都在使用pre-commit技术。于是,就朝着这个方向去研究。于是抽空写了脚本,在提交的…

【golang】数组和切片底层原理

数组类型的值(以下简称数组)的长度是固定的,而切片类型的值(以下简称切片)是可变长的。 数组的长度在声明它的时候就必须给定,并且之后不会再改变。可以说,数组的长度是其类型的一部分。比如&a…

【C语言】扫雷 小游戏

文章目录 一、游戏规则二、 代码逻辑三、游戏实现1. 游戏菜单设计2.设计雷区并随机布置雷(1) 设置雷区(2) 布置雷 3.排查雷 四、源码 一、游戏规则 1. 在9*9的小格子中,任意选取一个坐标(格子),选择后发现,如果没点中雷…

Substack 如何在去中心化内容创作领域掀起波澜

面对数字内容广告化的困境,Substack回归做内容的初心,通过产品和平台双轮驱动,重塑一个去中心化的多元文化内容聚集地,实现了增长突破。其核心策略在于先使用简洁的创作工具赋能内容生产,进而通过平台的互动机制促进用…

图像处理技巧形态学滤波之膨胀操作

1. 引言 欢迎回来,我的图像处理爱好者们!今天,让我们继续研究图像处理领域中的形态学计算。在本篇中,我们将重点介绍腐蚀操作的反向效果膨胀操作。 闲话少说,我们直接开始吧! 2. 膨胀操作原理 膨胀操作…

【JVM】JVM中的分代回收

文章目录 分代收集算法什么是分代分代收集算法-工作机制MinorGC、 Mixed GC 、 FullGC的区别是什么 分代收集算法 什么是分代 在java8时,堆被分为了两份: 新生代和老年代【1:2】 其中: 对于新生代,内部又被分为了三…

Object.assign详解

一、Object.assign是什么? Object.assign( )方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。 二、用法 Object.assign(target, ...sources) 参数:target ——>目标对象 source ——>源对象 返回值:…