大家好, 在这篇文章中,我会谈谈如何为你的项目达到100%的代码覆盖率。在这里介绍的方法将帮助你尽快达到这个目标。让我们开始吧!
要实现100%的覆盖,很明显,你需要做些准备。识别以下组件,如果立即准备好这些组件,可以加快这个过程。
-
我们正在测试的是:这里值得确定测试的代码可以是。它可以是一个函数,也可以是一个包含多种函数、循环比如的独立模块。
-
我们需要哪些第三方库:现在有很多库可以供用户测试代码,比如Mocha。
- 我应该用什么格式生成报告?:通常,像 Codecov 这样的服务需要生成 lcov 格式的报告。
一开始就明确了这一点,写测试就会变得简单得多,你就知道你在做什么以及为什么做。
现在,我们可以直接进入实践部分了。在这里,我会告诉你我是怎么做的。你也可以这样做。还有些小技巧可供参考。
来练练首先,我需要测试带有 typescript 扩展的文件。这个文件可以在这里找到 here:
(点击可查看图像)
接下来,为了测试这种情况,我在项目根目录下创建了一个文件夹,名为 test
。在那里,你可以放置专门用于测试的文件,例如 .test.ts
。这与普通的 Typescript 文件相同,但区别在于它仅用于测试。有时他们不加 test,而是加 spec,但是我建议你还是创建带有这种扩展名的文件。
现在,我们需要弄清楚一般如何测试。我们将使用Mocha、Sinon,并来生成C8报告:
"devDependencies": {
"@types/mocha": "^10.0.9",
"@types/sinon": "^17.0.3",
"c8": "^10.1.2",
"mocha": "^10.8.2",
"sinon": "^19.0.2"
}
切换到全屏模式,退出全屏
目前,我们需要把这些包连接起来,然后随着文章的推进,我们会逐渐添加更多库。
我们现在需要编写启动测试并生成报告的适当命令。以下是一些命令的列表:
"scripts": {
"test": "mocha --require ts-node/esm --experimental-specifier-resolution=node", // 运行测试脚本
"test:watch": "mocha --watch --require ts-node/esm --experimental-specifier-resolution=node", // 监视并运行测试脚本
"coverage": "c8 --reporter=lcov npm run test", // 生成代码覆盖率报告
"coverage:default": "c8 npm run test" // 默认生成代码覆盖率报告
},
全屏模式 退出全屏
非常有用的命令是 test:watch
。在测试代码时,记得用这个命令,如果不使用它,每次测试后都得手动重启,这会让你懒得测试。
很明显,要让typescript编译成普通的javascript,你需要安装一些额外的模块。
"devDependencies": {
"ts-node": "^10.9.2",
"typescript": "^5.6.3"
}
// 开发依赖项,包括ts-node和typescript的版本信息
全屏显示 退出全屏
现在,我们直接来看看这个文件。假设一切准备就绪,我们现在来测试一下这个函数:
add.test.ts
export function add(a: number, b: number): number {
return a + b;
}
全屏模式,退出全屏
要做到这一点,我们在测试用的文件中写入以下内容:
添加.ts
import { strict as assert } from 'assert';
import { add } from '../add';
describe('add() 函数测试', () => {
it('当把 2 和 3 相加时,结果应为 5', () => {
const result = add(2, 3);
assert.equal(result, 5);
});
it('当把 -1 和 1 相加时,应该得到 0', () => {
const result = add(-1, 1);
assert.equal(result, 0);
});
it('当把 -2 和 -3 相加时,结果应为 -5', () => {
const result = add(-2, -3);
assert.equal(result, -5);
});
it('当将 1.5 和 2 相加时,结果应为 3.5', () => {
const result = add(1.5, 2);
assert.equal(result, 3.5);
});
});
全屏 全屏退出
我们比较预期的结果和实际得到的结果。如果它们不同,那么测试就未通过,然后一切都会出错。这就是个玩笑,如果你创建了一个新功能并对其进行扩展,那么你需要确保旧的测试也能通过,这样才能确保代码没有问题。
如果我们在这个文件中有一个功能,那么实际上我们已经完全测试了它,也就是说,我们已经对整个文件进行了全面的测试。但是,当然,这是一个简单的例子,或者当需要操作DOM元素时呢?例如,模拟一个元素的click
行为,或者检查一个元素是否有class
。为此,你还需要安装下面描述的包:
"开发依赖": {
"@types/node": "^22.9.0",
"jsdom": "^25.0.1",
"jsdom-global": "^3.0.2",
}
进入全屏,退出全屏
这两个包将使我们在Node.js中工作时,感觉就像在操作我们在网站上看到的真实DOM一样(当然,也有一些限制)。我们先来尝试测试一下元素的点击事件,并简单配置这两个包。首先,这两个包。
require("jsdom-global")();
// 引入jsdom-global模块,并将全局的DOMParser设置为window.DOMParser
global.DOMParser = window.DOMParser;
全屏模式 退出全屏
我们将替换 DOMParser
,这样我们模块中的函数会使用这个替换版本,而不是在 Node.js 中的“未定义”。
现在,我们用一个具体的例子来试试整个事情。
setupClickHandler.ts
export function 设置点击事件处理器(buttonId: string, callback: () => void): void {
const button = document.getElementById(buttonId);
if (!button) {
throw new Error(`id为\"${buttonId}\"的按钮找不到`);
}
button.addEventListener('click', callback);
}
切换到全屏 按钮 退出全屏
setupClickHandler.test.ts
import { strict as assert } from 'assert';
import sinon from 'sinon';
import { setupClickHandler } from '../domManipulator';
import 'jsdom-global/register';
describe('setupClickHandler()', () => {
let button: HTMLElement;
beforeEach(() => {
document.body.innerHTML = `
<button id="testButton">点击我</button>
`;
button = document.getElementById('testButton')!;
});
afterEach(() => {
document.body.innerHTML = '';
});
it('应将点击处理程序附加到按钮上', () => {
const callback = sinon.spy();
setupClickHandler('testButton', callback);
button.click();
assert.equal(callback.calledOnce, true);
});
it('如果找不到按钮,应抛出错误', () => {
assert.throws(() => {
setupClickHandler('nonExistentButton', () => {});
}, /找不到具有 id "nonExistentButton" 的按钮/);
});
it('应正确处理多次点击事件', () => {
const callback = sinon.spy();
setupClickHandler('testButton', callback);
button.click();
button.click();
assert.equal(callback.callCount, 2);
});
});
进入全屏模式, 退出全屏
现在,我们可以轻松地测试DOM的行为。但在测试过程中,还有一个处理异步功能的需要。是的,这个话题很复杂,因为即使测试API也需要花费很多时间,但在这里你可以稍微作弊,模拟服务器的行为。为此,我们需要安装以下包:
"devDependencies": {
"nock": "^13.5.6",
"node-fetch": "^2.7.0",
}
全屏 退出全屏
Nock将允许你复制一个API,这个复制的API会返回我们设定的响应。node-fetch包将简单地用能在浏览器中工作的fetch
替换原有的,就像它在浏览器里那样工作。
让我们来设置这些软件包,
// 引入 node-fetch 模块
import fetch from "node-fetch";
// 将全局的 fetch 函数替换成 node-fetch
global.fetch = fetch as any;
点击全屏,然后退出全屏
那么我们来看看这个例子:
fetchData.ts
(获取数据的文件,通常用于TypeScript)
import fetch from 'node-fetch';
export async function fetchData(url: string): Promise<any> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP 错误!状态码为:${response.status}`);
}
return response.json();
}
进入全屏 退出全屏
获取数据测试.ts
import { strict as assert } from 'assert';
import nock from 'nock';
import { fetchData } from '../fetchData';
describe('fetchData()', () => {
const baseUrl = 'http://testapi.com';
beforeEach(() => {
nock.cleanAll();
});
it('应在响应成功时返回数据内容', async () => {
const mockData = { message: 'Success' };
nock(baseUrl)
.get('/endpoint')
.reply(200, mockData);
const data = await fetchData(`${baseUrl}/endpoint`);
assert.deepEqual(data, mockData);
});
it('应在响应不成功时抛出异常', async () => {
nock(baseUrl)
.get('/endpoint')
.reply(404);
await assert.rejects(
fetchData(`${baseUrl}/endpoint`),
/HTTP error! status: 404/
);
});
it('应能处理网络错误', async () => {
nock(baseUrl)
.get('/endpoint')
.replyWithError('Network error');
await assert.rejects(
fetchData(`${baseUrl}/endpoint`),
/网络错误/
);
});
});
全屏模式 退出全屏模式
在这里,我们检查函数是否按预期工作,从API请求数据。如果返回HTTP状态码200,检查是否有错误;如果不是,检查其他问题。总的来说,建议在测试时,最好不要向真正的服务器发送请求,因为这不仅不稳定,还不可预测,所以最好自己搭建一个以避免各种错误。这将更快且更稳定。
我还注意到测试中有许多重复的部分,你可以把这些测试放到一个单独的函数里,并在代码中调用它,这样会更自然。
函数文件:functions.ts
const e = (text: string, block: () => unknown, message: string) => {
// 定义了一个名为 e 的函数,用于对特定的 block 函数进行断言测试,确保其会抛出带有特定 message 的异常。
it(text, () => {
assert.throws(block, {
message: message
});
});
};
进入全屏
退出全屏
文件名:compile.test.ts
describe("编译函数测试", () => {
e(
"如果 TEMPLATE 不是字符串,则会抛出错误",
() => compile(123 as any),
`${COMPILE_ERROR}: 模板未找到或传递的值类型不是字符串`
);
全屏显示 退出全屏
这样我们就可以少写很多代码了。
现在我们的测试已经准备好了,我们需要通过Codecov来设置它们的执行。
与 Codecov 的集成:首先,我们需要一个代码库。你可以使用不同的服务,但我会用GitHub来展示。你需要访问网站并以你方便的方式进行注册。之后,你会看到一个类似的个人账户页面:
在这里,点击配置按钮,然后按照那里的步骤操作。我用的是GitHub Actions来配置,这样就会自动上传报告。下面是GitHub Actions的样子:
name: CI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
test:
name: 覆盖率
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: npm install
run: npm install
- name: npm run coverage
run: npm run coverage
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
点击全屏,点击退出全屏
每次提交时,它会自动运行并加载。如果一切顺利的话,你就可以为自己感到自豪,并在 README 中添加一个徽标,表明一切都经过测试哦耶!
因此,你将能够做所有酷炫的事情,而这样的建议不仅适用于JavaScript,也适用于其他编程语言,不仅适用于文件夹结构,也适用于函数的分离。希望你能真的做得非常酷,项目测试覆盖率能到100%。
如果这篇文章对你有帮助的话,可以通过给这个项目点个星 (☆) 来支持作者一下。谢谢你的支持!
谢谢大家来看这篇文章!
共同学习,写下你的评论
评论加载中...
作者其他优质文章