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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习算法informer(时序预测)(三)(Encoder)

一、EncoderLayer架构如图&#xff08;不改变输入形状&#xff09; 二、ConvLayer架构如图&#xff08;输入形状中特征维度减半&#xff09; 三、Encoder整体 包括三部分 1. 多层EncoderLayer 2. 多层ConvLayer 3. 层归一化 代码如下 class AttentionLayer(nn.Module):de…

世界奇观短视频制作,AI加持,新手也能月入上万

在这个数字化的时代&#xff0c;短视频已经成为了人们获取信息和娱乐的重要途径。特别是那些展示世界奇观的短视频&#xff0c;如极端的气候、危险的动物、美丽的自然景观等&#xff0c;这些主题具有很强的吸引力&#xff0c;能够引起观众的兴趣和好奇心。那么&#xff0c;如何…

运算放大器(运放)反相放大器电路

运算放大器(运放)反相放大器电路 设计目标 输入ViMin输入ViMax输出VoMin输出VoMax频率f电源Vcc电源Vee–7V7V–14V14V3kHz15V–15V 设计说明 该设计将输入信号 Vi 反相并应用 –2V/V 的信号增益。输入信号通常来自低阻抗源&#xff0c;因为该电路的输入阻抗由输入电阻器 R1…

深度学习神经网络协同过滤模型(NCF)与用户协同过滤(UCF)的区别

一、效果图 点我查看在线demo 二、启发式推荐系统 推荐系统的核心是根据用户的兴趣需求&#xff0c;给用户推荐喜欢的内容。常用的推荐算法有启发式推荐算法&#xff0c;可分为基于用户的 协同过滤&#xff0c;基于物品的协同过滤。 1、基于用户的协同过滤&#xff08;UCF…

【云岚到家】-day04-1-数据同步方案-Canal-MQ

【云岚到家】-day04-1-数据同步方案-Canal-MQ 1 服务搜索1.1 服务搜索技术方案1.1.1 需求分析1.1.2 技术方案1.1.2.1 使用Elasticsearch进行全文检索1.1.2.2 索引同步方案 1.1.3 CanalMQ1.1.3.1 MySQL主从数据同步1.1.3.2 Canal工作流程1.1.3.3 具体实现方案 1.2 MQ技术方案1.2…

Linux连接工具MobaXterm详细使用教程

目录 一、MobaXterm的下载 1、访问官网 2、下载便携版 3、启动MobaXterm 二、MobaXterm基本使用设置 1、新建会话 2、使用ssh连接第一个会话 3、设置主密码 4、主界面 5、sftp文件上传下载 6、文件拖拽的上传下载 7.右键粘贴 8、查看服务器监测信息​编辑 9、个…

文件扫描工具哪个好?便捷的文件扫描工具推荐

对于初入职场的大学毕业生&#xff0c;申请就业补贴是一项不可忽视的福利。 它不仅能够为新生活带来经济上的缓解&#xff0c;也有助于职业生涯的顺利起步。面对申请过程中需提交的文件&#xff0c;如纸质劳动合同&#xff0c;不必烦恼。市面上众多文件扫描软件能助你一臂之力…

Oracle最终还是杀死了MySQL

起因 大约15年前&#xff0c;Oracle收购了Sun公司&#xff0c;从而也拥有了MySQL&#xff0c;互联网上关于Oracle何时会“扼杀MySQL”的讨论此起彼伏。 当时流传着各种理论&#xff1a;从彻底扼杀 MySQL 以减少对 Oracle 专有数据库的竞争&#xff0c;到干掉 MySQL 开源项目&…

vcpkg安装opencv中的特殊问题记录(无法找到opencv_corexd.dll)

我是按照网上的vcpkg安装opencv方法进行的&#xff08;比如这篇&#xff1a;从0开始在visual studio上安装opencv&#xff08;超详细&#xff0c;针对小白&#xff09;&#xff09;&#xff0c;但是中间出现了一些别人没有遇到的问题&#xff0c;虽然原因没有找到&#xff0c;但…

离子污染测试仪有哪些检测方法?校准机构如何选择?

离子污染测试仪是许多生产企业会使用到的一种仪器&#xff0c;作为一种高精度的操作仪器&#xff0c;在长时间的使用下&#xff0c;仪器磨损和失准也是常见状况&#xff0c;因此企业都会进行定期校准来维护仪器&#xff0c;那么离子污染测试仪有哪些检测方法&#xff1f;校准机…

深度学习500问——Chapter11:迁移学习(3)

文章目录 11.3 迁移学习的常用方法 11.3.1 数据分布自适应 11.3.2 边缘分布自适应 11.3.3 条件分布自适应 11.3.4 联合分布自适应 11.3.5 概率分布自适应方法优劣性比较 11.3.6 特征选择 11.3.7 统计特征对齐方法 11.3 迁移学习的常用方法 11.3.1 数据分布自适应 数据分布自适…

阿里拍卖资产推荐算法 召回进展年中总结

阿里拍卖是阿里巴巴旗下拍卖平台&#xff0c;覆盖房产、机动车、土地、债权等类目。召回策略作为推荐场景的第一环&#xff0c;决定了整个推荐系统的上限&#xff0c;目前包含了包括向量召回、I2I、LBS2I、C2I等多路召回。召回的核心目标是尽可能的返回用户所有可能会感兴趣的商…

教你使用Python玩转MySQL数据库,大数据导入不再是难题!

数据分析离不开数据库&#xff0c;如何使用python连接MySQL数据库&#xff0c;并进行增删改查操作呢&#xff1f; 我们还会遇到需要将大批量数据导入数据库的情况&#xff0c;又该如何使用Python进行大数据的高效导入呢&#xff1f; 本文会一一讲解&#xff0c;并配合代码和实…

Spring Boot组件化与参数校验

Spring Boot组件化与参数校验 Spring Boot版本选择 2.3.x版本 2.6.x版本 Spring Boot核心思想 约定大于配置&#xff0c;简化繁琐的配置 Spring Boot自动配置原理 SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类&#xff0c;Spr…

Vue3学习日记(day5)

接下来我们继续探讨文档 event对象 在Vue.js中&#xff0c;$event变量或箭头函数中的event参数用于捕获原始的DOM事件对象。这个对象包含了所有与特定事件相关的信息&#xff0c;比如鼠标点击的位置、键盘按键的键码、触摸事件的触摸点等。 当你在事件处理器中需要做一些基于…

前端时钟页面(JSP语言)

前端时钟页面(JSP语言) 一、效果图 二、介绍 1.目前市面上很多时钟组件&#xff0c;像电子时钟&#xff0c;3D时钟&#xff0c;Echarts画的时钟 2.这款时钟&#xff0c;是本人多年前寻找并修改的&#xff0c;感觉效果还不错 3.目前这是jsp写的&#xff0c;后面有时间会用Vue写…

口罩佩戴智能监测摄像机

智能监测摄像机在现代城市安全管理中扮演着关键角色&#xff0c;尤其是像口罩佩戴智能监测摄像机这样的设备&#xff0c;其应用正在日益扩展&#xff0c;对于公共卫生和安全至关重要。 这类摄像机利用先进的图像识别技术&#xff0c;能够实时监测人群中是否佩戴口罩。通过高精度…

CVPR2023论文速览Scenes相关49篇

CVPR2023论文速览Scenes Paper1 CLIP2Scene: Towards Label-Efficient 3D Scene Understanding by CLIP 摘要原文: Contrastive Language-Image Pre-training (CLIP) achieves promising results in 2D zero-shot and few-shot learning. Despite the impressive performance …

05. Java多线程 join 方法

1. 前言 本节对 join 方法进行深入的剖析&#xff0c;主要内容点如下&#xff1a; 了解 join 方法的作用&#xff0c;初步的理解 join 方法的使用带来的效果是学习本节内容的基础&#xff1b;了解 join 方法异常处理&#xff0c;我们在使用 join 方法是&#xff0c;需要对 jo…

【odoo】常用的字符转义:“>“,“<“,““,“/“等

概要 字符转义是指在编写代码或处理文本数据时&#xff0c;将特殊字符转换为另一种形式&#xff0c;以便在特定的上下文中正确解析和处理这些字符。 内容 特殊字符描述XML转义表示法&和符号&amp;<小于符号<>大于符号>"双引号&quot;单引号&ap…