vscode插件开发之 - TestController
TesController概要介绍
TestController 组件是用于实现自定义测试框架和集成测试结果的。它允许开发者定义自己的测试运行器,以支持在VSCode中运行和展示测试。以下是一些使用 TestController 组件的主要场景:
自定义测试框架:如果你正在开发或使用一个非标准的测试框架,你可以使用 TestController 来集成这个框架的测试结果。
语言特定的测试:对于某些语言或框架,VSCode可能没有内置的测试支持。使用 TestController,你可以为这些语言或框架添加测试支持。
集成外部测试工具:如果你需要在VSCode中展示由外部测试工具生成的测试结果,TestController 可以用来映射这些结果到VSCode的测试UI。
测试结果可视化:通过 TestController,你可以控制测试结果如何在VSCode的测试面板中展示,包括测试的通过、失败、跳过等状态。
如何开发一款自定义的测试框架插件
要开发一款基于vscode的自定义测试框架非常简单,有三个步骤。以读取markdown中的代码,执行测试为例子来看看如何自定义测试框架插件
步骤一:解析文档中内容,并将结果添加到testcontroller的testItem对象
下面的代码中,对给定的文档内容通过正则表达式进行match,获取到markdown文件中的测试name,experssion,expected内容,并将parse出来的内容用于构建TestItem。下图图一假设是文本上的代码内容,下图图二是parse出来的Test对象内容。


function loadTestsFromDocument(testController: vscode.TestController, document: vscode.TextDocument) {const tests = parseTests(document.getText());for (const test of tests) {const testItem = testController.createTestItem(test.name, test.expression + "=" + test.expected);testController.items.add(testItem);}
}interface Test {name: string;expression: string;expected: number;
}function parseTests(text: string): Test[] {const testRegex = /^(\d+ \+ \d+) = (\d+) \/\/ (.+)$/gm;const tests: Test[] = [];let match;while ((match = testRegex.exec(text)) !== null) {tests.push({name: match[3],expression: match[1],expected: parseInt(match[2])});}return tests;
}
上述代码中,TestController.createTestItem 方法用于创建一个新的 TestItem,它代表一个测试用例。以下是 createTestItem 方法的一些关键参数和它们的说明:输入参数:
id (string): 测试用例的唯一标识符。这里是用Test.name作为id
label (string): 测试用例的显示名称,通常在UI中展示给用户。这里是组装成的label信息,后面要通过解析label信息来执行测试。testItem.label=test.expression + "=" + test.expected
uri (vscode.Uri): 表示测试用例所属文件的位置。通常使用当前编辑器的文档URI。
range (vscode.Range): 测试用例在文件中的位置范围。这有助于用户快速定位到测试用例的代码。
children (TestItem[], optional): 如果这个测试用例是一个容器,比如一个测试套件,你可以在这里提供子测试用例的数组。
tags (string[], optional): 一组标签,可以用来对测试用例进行分类或标记。
步骤二:自定义测试执行逻辑
下面定义了一个简单的runTest逻辑,通过解析testItem.label信息,判断expected和actual的值是否相等来判断测试执行结果。
async function runTest(testItem: vscode.TestItem) {const expression = testItem.label.split('=')[0].trim();const expected = parseInt(testItem.label.split('=')[1].trim());const actual = eval(expression);const result = actual === expected;if (result) {testItem.busy = false;return `${testItem.label}: PASSED`;} else {testItem.busy = false;return `${testItem.label}: FAILED`;}
}
步骤三:注册命令执行测试
定义好前面的内容后,就可以注册命令,将testController.items的内容转换成数组,在逐个执行runTest方法,并把执行结果通过showInfomationMessage显示出来。
vscode.commands.registerCommand('markdownTestController.runTests', async () => {const tests = Array.from(testController.items)const results: string[] = [];for (const [, testItem] of tests) {vscode.window.showInformationMessage(JSON.stringify(testItem));const resultMessage = await runTest(testItem);results.push(resultMessage);}if (results.length > 0) {vscode.window.showInformationMessage(results.join('\n'));} else {vscode.window.showInformationMessage('No tests executed.');}});
}
编写好脚本后,就可以执行了,在markdown文件中准备了一个数学计算,然后执行命令,可以看到message中显示执行结果,另外,为了调试,这里还显示testItem对象的值。

除了自定义测试执行逻辑,实际在开发vscode测试相关类插件时,还可以调用第三方已有的测试工具执行测试,代码的例子是调用jest执行测试的例子。
上面只定义了一个简单的runTest逻辑,在实际项目中,更多的是集成第三方测试执行插件,例如集成jest,在runTest方法里面只需通过“child_process.exec(`npx jest -t "${testItem.label}" --json`”来执行对应的测试即可。下面的使用vscode的testcontroller等组件,集成test来执行测试的例子。所有代码如下所示:
import * as vscode from 'vscode';
import * as child_process from 'child_process';export function activate(context: vscode.ExtensionContext) {const testController = vscode.tests.createTestController('jestTestController', 'Jest Tests');context.subscriptions.push(testController);context.subscriptions.push(vscode.commands.registerCommand('extension.runJestTests', async () => {await runAllTests(testController);}));async function runAllTests(testController: vscode.TestController) {const testItems: vscode.TestItem[] = [];testController.items.forEach(testItem => testItems.push(testItem));const request = new vscode.TestRunRequest(testItems);const run = testController.createTestRun(request);let allTestsPassed = true;const testResults: { [key: string]: boolean } = {};for (const test of testItems) {run.started(test);const result = await runJestTest(test);testResults[test.id] = result;if (result) {run.passed(test);} else {run.failed(test, new vscode.TestMessage('Test failed'));allTestsPassed = false;}}run.end();if (allTestsPassed) {vscode.window.showInformationMessage('All tests passed.');} else {vscode.window.showInformationMessage('Some tests failed. Check test results for details.');}}context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(doc => {if (doc.languageId === 'typescript' || doc.languageId === 'javascript') {loadTestsFromDocument(testController, doc);}}));vscode.workspace.textDocuments.forEach(doc => {if (doc.languageId === 'typescript' || doc.languageId === 'javascript') {loadTestsFromDocument(testController, doc);}});
}function loadTestsFromDocument(testController: vscode.TestController, document: vscode.TextDocument) {const tests = parseTests(document.getText());for (const test of tests) {const testItem = testController.createTestItem(test.name, test.name, document.uri);testController.items.add(testItem);}
}interface Test {name: string;
}function parseTests(text: string): Test[] {const testRegex = /test\(['"](.+)['"]/g;const tests: Test[] = [];let match;while ((match = testRegex.exec(text)) !== null) {tests.push({name: match[1]});}return tests;
}async function runJestTest(testItem: vscode.TestItem): Promise<boolean> {return new Promise((resolve) => {const options = {cwd: vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined,};child_process.exec(`npx jest -t "${testItem.label}" --json`, options, (err, stdout, stderr) => {if (err) {console.error(stderr);vscode.window.showErrorMessage(`Test failed to run: ${stderr}`);resolve(false);} else {try {const result = JSON.parse(stdout);vscode.window.showInformationMessage(`${testItem.label}: ${result.numFailedTests === 0 ? 'Passed' : 'Failed'}`);resolve(result.numFailedTests === 0);} catch (parseError) {console.error(`Failed to parse test result: ${parseError}`);vscode.window.showErrorMessage(`Failed to parse test result: ${parseError}`);resolve(false);}}});});
}export function deactivate() { }
为了验证上面的插件是否工作,需要再准备一个包含jest测试的项目,该项目包含一个简单sum函数,以及用jest框架测试add函数的测试脚本。
import { add } from './adder';test('first', () => {expect(add(1, 2)).toBe(4);
});test('second', () => {expect(add(0, 0)).toBe(0);
});
运行插件,可以看到执行了两个测试,其中一个成功,一个失败,说明成功调用jest执行了测试,并获取到了测试结果。结果如下图所示:

以上就是vscode插件开发TestController组件的使用介绍,在实际项目,例如playwright-vscode插件就会使用这些组件,完成对ui测试的执行。后续的博客将从源码层面来解析playwright-vscode插件实现原理。
相关文章:
vscode插件开发之 - TestController
TesController概要介绍 TestController 组件是用于实现自定义测试框架和集成测试结果的。它允许开发者定义自己的测试运行器,以支持在VSCode中运行和展示测试。以下是一些使用 TestController 组件的主要场景: 自定义测试框架:如果你正在开发…...
QBitArray使用详解
QBitArray使用详解 一、创建和初始化 QBitArray1.1 QBitArray默认构造1.2 QBitArray指定大小的构造1.3 QBitArray指定大小和初始值的构造 二、设置和访问位2.1 QBitArray设置单个位2.2 QBitArray访问单个位2.3 QBitArray使用下标操作符 三、设置所有位3.1 QBitArray将所有位设置…...
基于Python的自然语言处理项目 ChatTTS 推荐
**项目名称:ChatTTS** ChatTTS是一个基于Python的自然语言处理项目,旨在实现一个简单的文本到语音转换系统。它使用深度学习技术,通过自然语言处理和语音合成算法,将文本转换为语音输出。 **项目介绍**: Chat…...
论 To B 产品:从概念到市场实践
本文作者为 360 奇舞团产品经理 论 To B 产品:从概念到市场实践 To B 产品在商业世界中扮演着至关重要的角色。相较于面向消费者的To C市场,To B市场更专注于为其他企业提供产品和服务。理解和成功运营To B产品需要对其特定的市场需求和运作方式有深刻的…...
如何通过自定义模块DIY出专属个性化的CSDN主页?一招教你搞定!
个人主页:学习前端的小z 个人专栏:HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 💯如何通过HTMLCSS自定义模板diy出自己的个性化csdn主页&#x…...
[BSidesCF 2020]Had a bad day1
看到页面有两个按钮 先随便点一个试一下,当我们点击之后发现url是有变动的 感觉url是有点东西的,可能是某种注入,先尝试一下sql注入,发现给出了报错 通过报错我们可以确定是文件包含漏洞,那我们试试php伪协议去读取一下…...
从媒体网站的频道划分看媒体邀约的分类?
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 媒体宣传加速季,100万补贴享不停,一手媒体资源,全国100城线下落地执行。详情请联系胡老师。 在我们举行活动的时候,通常会邀请媒体到现场来…...
Day40
Day40 监听器 概念: 监听器用于监听web应用中某些对象信息的创建、销毁、增加,修改,删除等动作的 发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用 监听器对象中的方法。 常用于统计在线…...
linux基础 - 内核的基础概念
目录 零. 前言 一. 源码简介 二. 存储管理 物理内存管理: 虚拟内存管理: 内存分配与回收: 三. CPU 和进程管理 进程管理: CPU 管理: 四. 文件系统 文件系统的概念 常见的 Linux 文件系统类型 文件系统的工…...
centos7系统使用docker-compose安装部署jenkins
CentOS7系统使用docker-compose安装部署jenkins,并实现前后端自动构建 记录一次工作中部署jenkins的真实经历,总结了相关经验 1.准备环境 1.java 由于最新的jenkins需要jdk11以上才能支持,而系统里的jdk是1.8的,因此等jenkins安…...
传染病报卡内容——丙型
--丙型 select a.morbiditdate 发病日期, diagnosedate 诊断日期, a.deathdate 死亡日期, a.casetypequality 病例分类,a.hcvrna "HCR_RNA定量" from zl_sdmb.t_报卡记录 t, c1_infectiousv1_6 a where t.id a.fileid and t.卡片种类 传…...
本地快速部署大语言模型开发平台Dify并实现远程访问保姆级教程
文章目录 前言1. Docker部署Dify2. 本地访问Dify3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 前言 本文主要介绍如何在Linux Ubuntu系统使用Docker快速部署大语言模型应用开发平台Dify,并结合cpolar内网穿透工具实现公网环境远程访问…...
《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 02 Clos拓扑
本章回答以下问题: 什么是 Clos 拓扑,它与“接入 - 汇聚 - 核心”拓扑有何不同?Clos 拓扑的特征是什么?Clos 拓扑对数据中心网络的影响是什么? Clos拓扑 云原生数据中心基础设施的先行者们想要构建一种支持大规模水平扩展网络。 基本的Clos拓扑如图…...
VUE3版本新特性
VUE3版本新特性 VUE3和VUE2的区别路由的使用vite安装项目新特性使用 1.VUE3和VUE2的区别 2020年9月18日,Vue.js发布版3.0版本,代号:One Piece 于 2022 年 2 月 7 日星期一成为新的默认版本! Vue3性能更高,初次渲染快55%, 更新渲染快133% 。…...
【Oracle篇】Oracle数据库坏块处理:rman修复坏块实践与案例分析(第七篇,总共八篇)
💫《博主介绍》:✨又是一天没白过,我是奈斯,DBA一名✨ 💫《擅长领域》:✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux,也在扩展大数据方向的知识面✌️…...
学懂C#编程:从一个简单的例子理解事件处理
在C#中,事件是一种特殊的委托类型,用于在对象上发生某些事情时通知订阅者。事件的处理通常包括定义事件,创建触发事件的条件,以及订阅该事件的事件处理程序。 以下是一个简单的C#事件处理示例: using System;// 定义…...
深入理解指针(2)
4. const 修饰指针 4.1 const修饰变量 变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤。 …...
C#.Net筑基-集合知识全解
01、集合基础知识 .Net 中提供了一系列的管理对象集合的类型,数组、可变列表、字典等。从类型安全上集合分为两类,泛型集合 和 非泛型集合,传统的非泛型集合存储为Object,需要类型转。而泛型集合提供了更好的性能、编译时类型安全…...
AI PPT生成器,一键在线智能生成PPT工具
PPT作为商业沟通和教育培训中的重要工具,PPT制作对于我们来说并不陌生。但是传统的PPT制作不仅耗时,而且想要做出精美的PPT,需要具备一定的设计技能。下面小编就来和大家分享几款AI PPT工具,只要输入主题,内容就可以在…...
stm32学习笔记---零基础入门介绍2
目录 STM32介绍 STM32家族系列 ARM介绍 ARM内核型号种类 我们学习用的STM32 片上资源/外设(Peripheral) 命名规则 系统结构 引脚定义 STM32的启动配置 STM32最小系统电路和其他部分电路 最小系统板的实物图 附:安装软件准备 声明…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
Linux入门课的思维导图
耗时两周,终于把慕课网上的Linux的基础入门课实操、总结完了! 第一次以Blog的形式做学习记录,过程很有意思,但也很耗时。 课程时长5h,涉及到很多专有名词,要去逐个查找,以前接触过的概念因为时…...
简单聊下阿里云DNS劫持事件
阿里云域名被DNS劫持事件 事件总结 根据ICANN规则,域名注册商(Verisign)认定aliyuncs.com域名下的部分网站被用于非法活动(如传播恶意软件);顶级域名DNS服务器将aliyuncs.com域名的DNS记录统一解析到shado…...
安宝特方案丨从依赖经验到数据驱动:AR套件重构特种装备装配与质检全流程
在高压电气装备、军工装备、石油测井仪器装备、计算存储服务器和机柜、核磁医疗装备、大型发动机组等特种装备生产型企业,其产品具有“小批量、多品种、人工装配、价值高”的特点。 生产管理中存在传统SOP文件内容缺失、SOP更新不及、装配严重依赖个人经验、产品装…...
