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最小系统电路和其他部分电路 最小系统板的实物图 附:安装软件准备 声明…...
UniHacker:Unity引擎功能探索的技术研究指南
UniHacker:Unity引擎功能探索的技术研究指南 【免费下载链接】UniHacker 为Windows、MacOS、Linux和Docker修补所有版本的Unity3D和UnityHub 项目地址: https://gitcode.com/GitHub_Trending/un/UniHacker 技术研究免责声明 本指南所述工具及方法仅用于技术…...
Node RED实战:5分钟搞定MQTT消息发布与订阅(附EMQX配置)
Node RED与MQTT实战:从零构建物联网消息系统 1. 为什么选择Node RED与MQTT组合? 物联网开发领域一直存在一个核心挑战:如何快速搭建可靠的消息通信系统而不陷入底层协议实现的泥潭。这正是Node RED与MQTT这对黄金组合的价值所在——它们让开发…...
macOS Unlocker V3.0:在Windows和Linux上免费运行macOS虚拟机的终极解决方案 [特殊字符]
macOS Unlocker V3.0:在Windows和Linux上免费运行macOS虚拟机的终极解决方案 🚀 【免费下载链接】unlocker 项目地址: https://gitcode.com/gh_mirrors/unlo/unlocker macOS Unlocker V3.0是一款革命性的开源工具,让您能够在Windows或…...
为什么92%的Python WASM尝试失败?——资深编译器工程师披露LLVM-WASI链路5大隐性断点
第一章:Python WASM部署的现状与认知误区WebAssembly(WASM)正迅速成为浏览器端高性能计算的新基石,但将 Python 部署至 WASM 环境仍存在显著的认知断层。许多开发者误以为“Python 代码可直接编译为 WASM”,实则 Pytho…...
s2-pro部署实操:CSDN平台GPU资源监控与s2-pro服务性能关联分析
s2-pro部署实操:CSDN平台GPU资源监控与s2-pro服务性能关联分析 1. 专业语音合成工具s2-pro简介 s2-pro是Fish Audio开源的专业级语音合成模型镜像,它能够将文本转换为自然流畅的语音,并支持通过参考音频来复用特定音色。这个工具特别适合需…...
颈腰椎病引发 “耳后疼痛”:耳根刺痛,可能是颈椎在 “捣乱”
很多人出现耳后持续性刺痛或按压痛,会误以为是中耳炎、腮腺炎,实则部分耳后疼痛与颈椎病变相关。颈椎病变压迫枕大神经(从颈椎延伸至耳后),会导致神经分布区域疼痛;同时颈椎肌肉痉挛、僵硬,牵拉…...
音乐解密技术探秘:从加密困境到跨平台解决方案
音乐解密技术探秘:从加密困境到跨平台解决方案 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地址: https://gitc…...
FINCH聚类算法实战:5分钟搞定无参数聚类(附Python代码)
FINCH聚类算法实战:5分钟搞定无参数聚类(附Python代码) 在数据科学和机器学习领域,聚类分析一直是探索性数据分析的重要工具。传统聚类方法如K-means、DBSCAN等虽然广泛应用,但都面临一个共同挑战:需要人工…...
Fun-ASR-MLT-Nano-2512快速上手:Web界面操作,无需代码基础
Fun-ASR-MLT-Nano-2512快速上手:Web界面操作,无需代码基础 1. 语音识别新选择:Fun-ASR-MLT-Nano-2512 1.1 模型简介 Fun-ASR-MLT-Nano-2512是阿里通义实验室推出的轻量级多语言语音识别模型,经过开发者by113小贝的二次开发优化…...
华为光猫配置解密工具技术架构解析与实现机制
华为光猫配置解密工具技术架构解析与实现机制 【免费下载链接】HuaWei-Optical-Network-Terminal-Decoder 项目地址: https://gitcode.com/gh_mirrors/hu/HuaWei-Optical-Network-Terminal-Decoder 在网络设备运维领域,华为光猫配置文件的安全加密机制为设备…...
