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插件实现原理。

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

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

相关文章

C++语法19 循环嵌套结构(for/while循环)

语法阶段已经更新到第18章了&#xff0c;前面的知识你都学会了吗&#xff1f;如果还没有学习前面的知识&#xff0c;请点击&#x1f449;语法专栏进行学习哦&#xff01; 目录 循环嵌套 训练&#xff1a;数字矩形 解析 参考代码 训练&#xff1a;星号三角形 解析 参考代码 …

Scikit-Learn梯度提升决策树(GBDT)

Scikit-Learn梯度提升决策树 1、梯度提升决策树(GBDT)1.1、Boosting方法1.2、GBDT的原理1.3、GBDT回归的损失函数1.4、梯度下降与梯度提升1.5、随机森林与GBDT1.6、GBDT的优缺点2、Scikit-Learn梯度提升决策树(GBDT)2.1、Scikit-Learn GBDT回归2.1.1、Scikit-Learn GBDT回归…

【车载开发系列】CAN通信总线再理解(中篇)

【车载开发系列】CAN通信总线再理解&#xff08;中篇&#xff09; 九. CAN总线标准十. CAN物理层十一. CAN数据链路层1&#xff09;CAN的通信帧类型2&#xff09;CAN的标准帧格式1. CAN ID2. 数据场 3&#xff09;CAN总线仲裁 十二. CAN应用层1&#xff09;CANopen2&#xff09…

电力行业如何利用行列视(RCV)解决数据治理难题?

电力行业作为能源供应的重要支柱&#xff0c;面临着海量的数据处理和治理挑战。行列视&#xff08;RCV&#xff09;作为一种强大的数据应用系统&#xff0c;可以为电力行业提供有效的解决方案&#xff0c;以应对这些难题。以下是如何利用行列视&#xff08;RCV&#xff09;解决…

C语言中的位域(bit-field)是什么,以及它的用途和优缺点

在C语言中&#xff0c;位域&#xff08;bit-field&#xff09;是一种特殊的数据结构&#xff0c;它允许在结构体&#xff08;struct&#xff09;中定义其成员所占用的位数&#xff0c;而不是使用整个字节或更大的内存空间。位域通常用于存储布尔值、状态标志、硬件控制位等&…

负数在二进制中的表示方法

负数在二进制中的表示方法 1 补码介绍2 举例-10的二进制表示3 测试打印0xfffffff6值 1 补码介绍 在二进制补码表示法中&#xff0c;负数是通过取其绝对值的二进制表示&#xff08;即正数的二进制表示&#xff09;&#xff0c;然后取反&#xff08;0变1&#xff0c;1变0&#x…

《车载以太网通信测试》课程来袭!!!

本课程包含教程和脚本两部分内容。 教程 详细介绍以太网&#xff0c;如何理解TCP/IP协议&#xff0c;CAPL中涉及以太网的代码&#xff0c;以太网测试环境如何搭建&#xff0c;从物理层、链路层、网络层、传输层到应用层多种协议测试点的测试原理和测试方法介绍&#xff0c;中…

西门子学习笔记12 - BYTE-REAL互相转化

这是针对于前面MQTT协议的接收和发送数组只能是BYTE数组做出的对应的功能块封装。 1、BYTE-REAL转化 1、把byte数组转成字符串形式 2、把字符串转成浮点数 2、REAL-BYTE转化 1、把浮点数转成字符串 2、把字符串转成Byte数组

【转载】TIOBE 编程指数 6 月排行榜公布,vb.net排第九

原文地址&#xff1a;https://baijiahao.baidu.com/s?id1801368030428902126&wfrspider&forpc IT之家 6 月 9 日消息&#xff0c;TIOBE 编程社区指数是一个衡量编程语言受欢迎程度的指标&#xff0c;评判的依据来自世界范围内的工程师、课程、供应商及搜索引擎&#…

乐鑫esp32系列睡眠模式下蓝牙连接功耗测试,新支持ESP-C6,启明云端乐鑫代理商

本教程适用于ESP32-S3、ESP32-C3、ESP32-C6&#xff1b; 睡眠模式介绍 ESP32系列常见的休眠方式有三种&#xff0c;分别为Modem-sleep、Light-sleep 和 Deep-sleep。 Modem-sleep模式&#xff1a;CPU 正常工作&#xff0c;可以对时钟进行配置。 进入 Modem-sleep 模式后&…

【Android面试八股文】odex了解吗?解释型语言和编译型语言有什么区别?JAVA是编译型语言还是解释型语言?

文章目录 一、什么是 ODEX?二、解释型和编译型的区别2.1 解释型语言(Interpreted Language)2.2 编译型语言(Compiled Language)2.3 JAVA是编译型语言还是解释型语言?2.3.1 在标准JAVA环境2.3.1 在Android环境一、什么是 ODEX? ODEX 文件(Optimized DEX),表示经过优化…

大模型与知识图谱:联合打造智能未来,案例解析让你大开眼界!

在数字化浪潮中&#xff0c;大模型和知识图谱作为人工智能领域的两大核心技术&#xff0c;正逐渐改变着我们的生活。大模型&#xff0c;是一种通过大规模数据训练得到的深度学习模型&#xff0c;具备强大的语义理解和推理能力&#xff1b;而知识图谱&#xff0c;则是以图状结构…

对象存储测试工具-s3cmd

一、环境安装 官网&#xff1a;https://s3tools.org/s3cmd 下载安装包&#xff1a;https://s3tools.org/download GitHub&#xff1a;https://github.com/s3tools/s3cmd/releases 本文安装包&#xff1a;https://github.com/s3tools/s3cmd/releases/download/v2.0.2/s3cmd-2.0…

MyBatis打印不带问号SQL

mybatis-config.xml里配置自己家实现拦截器MyBatisUpdateInterceptor <plugins><plugin interceptor"project.x.interceptor.MyBatisUpdateInterceptor"/></plugins>applicationContext.xml里配置自己家实现拦截器的包路径 <context:component-…

光伏设计需要设计哪些方面?

光伏设计是一项复杂而关键的工作&#xff0c;它涉及到多个方面的综合考虑&#xff0c;以确保光伏系统的效率、安全性和可持续性。下面将详细探讨光伏设计需要设计的几个方面。 一、系统规模与布局 光伏设计的首要任务是确定系统的规模和布局。这包括评估安装地点的光照资源、可…

【nginx】 nginx核心功能

【nginx】 nginx核心功能 1.nginx核心功能 1. 反向代理 2. 负载均衡 3. 动静分离 4. nginx的高可用2. 反向代理 正向代理: 该服务器代理的是客户端&#xff0c;对于服务器来说&#xff0c;不知道真实客户端的ip。比如: 翻墙软件。 访问国外的服务器---使用了翻墙软件----对…

Vue 使用setInterval定时器导致前端页面卡死(解决方法)

Vue 使用setInterval定时器导致前端页面卡死 原因 setinterval不会清除定时器队列&#xff0c;每重复执行1次都会导致定时器叠加&#xff0c;最终卡死你的网页。 其原因与JS引擎线程有关&#xff08;需深入研究JS引擎线程&#xff09; &#xff0c;但是setTimeout是自带清除定…

3.4 SpringAOP之通知获取数据

1.JoinPoint和ProceedingJoinPoint接口 接口定义功能适用通知类型JoinPoint代表了在程序执行过程中的连接点获取到连接点&#xff08;被拦截方法&#xff09;的名称、参数、目标对象以及代理对象等信息前置、后置、返回后、抛出异常后通知ProceedingJoinPoint是 JoinPoint 接口…

CSS 变量的优势

CSS 变量(Custom Properties)&#xff1a;灵活的样式定义与复用 在 CSS 中,我们通常会使用一些固定的值来定义样式,比如颜色、字体大小、边距等。但随着项目的不断发展和需求的变化,这些固定值可能会变得难以维护和复用。这就是 CSS 变量(Custom Properties)派上用场的地方。 …

26.ByteBuf组成和内存释放源码

ByteBuf中有什么? 1.capacity 容量 2.max capacity 最大容量 3.读指针(在ByteBuf的一开始) 4.写指针(在ByteBuf的一开始) 读指针与写指针之间就是 还未读取的部分(可读部分)。 读指针读过的部分叫做废弃的部分。 ByteBuf由四部分组成 可扩容部分(容量capacity与…