大家好,在本文中,我将讨论如何为您的项目实现 100% 的代码覆盖率。这里描述的技术将使您能够尽快做到这一点。好吧,让我们开始吧!
动态图片
准备
为了确保 100% 覆盖,显然您需要做好准备。首先,有必要确定以下组件,如果立即做好准备,将加快此过程:
- 测试内容:这里需要确定要测试的代码片段。它可以是单个函数,也可以是包含一系列函数、循环等的独立模块。
- 我们需要哪些第三方库:今天,有许多库(例如Mocha等)允许用户测试代码。
- 我应该使用什么格式来生成报告:通常,对于像Codecov这样的服务,我们需要以
lcov格式生成报告。
如果一开始就决定了这一点,你编写测试就会变得更容易,因为你会明白你在做什么以及出于什么目的。
现在,值得进入实践部分。在其中,我将举一个例子来说明我如何进行测试,如何实现这一点,以及通常有哪些技巧可以实现这一点。
实践
因此,首先,我需要使用 typescript 扩展测试我拥有的文件。该文件可以在此处查看:
接下来,为了测试这种情况,我在项目的根结构中创建了一个文件夹。它被称为test。在那里,你可以给出特殊的分割,例如.test.ts。这是相同的 TypeScript 文件,但不同之处在于它仅用于测试。有时他们不添加测试,或者他们添加 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
export function add(a: number, b: number): number {
return a + b;
}
为此,我们在测试文件中写入以下内容:
添加.ts
import { strict as assert } from 'assert';
import { add } from '../add';
describe('Function add()', () => {
it('should return 5 when adding 2 and 3', () => {
const result = add(2, 3);
assert.equal(result, 5);
});
it('should return 0 when adding -1 and 1', () => {
const result = add(-1, 1);
assert.equal(result, 0);
});
it('should return -5 when adding -2 and -3', () => {
const result = add(-2, -3);
assert.equal(result, -5);
});
it('should return 3.5 when adding 1.5 and 2', () => {
const result = add(1.5, 2);
assert.equal(result, 3.5);
});
});
我们会比较预期结果和实际结果。如果结果不同,则测试未通过,然后一切都会失败。这是一个笑话,如果你创建了一个新功能,然后扩展它,那么你需要确保旧测试通过,这样你才能确保代码编写正确。
如果文件中有这个函数,那么实际上我们已经对它进行了全面测试,也就是说,我们已经用测试覆盖了整个文件。但是,当然,这是一个简单的例子,但如果我们需要使用 DOM 元素怎么办?例如,click在元素上模仿,或者检查是否存在class。为此,您还需要安装下面描述的软件包:
"devDependencies": {
"@types/node": "^22.9.0",
"jsdom": "^25.0.1",
"jsdom-global": "^3.0.2",
}
这两个包将允许我们在 Node.js 中工作,就像我们在网站上看到的真实 DOM 一样(当然有限制)。让我们尝试测试元素上的单击,并通常配置这两个模块:
require("jsdom-global")();
global.DOMParser = window.DOMParser;
在这里,我们将进行替换DOMParser,以便模块中的函数拾取它而不是 Node.js 中的未定义。
现在,让我们尝试用一个具体的例子来测试整个事情:
setupClickHandler.ts
export function setupClickHandler(buttonId: string, callback: () => void): void {
const button = document.getElementById(buttonId);
if (!button) {
throw new Error(`Button with id "${buttonId}" not found`);
}
button.addEventListener('click', callback);
}
设置ClickHandler.测试.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">Click Me</button>
`;
button = document.getElementById('testButton')!;
});
afterEach(() => {
document.body.innerHTML = '';
});
it('should attach a click handler to the button', () => {
const callback = sinon.spy();
setupClickHandler('testButton', callback);
button.click();
assert.equal(callback.calledOnce, true);
});
it('should throw an error if the button is not found', () => {
assert.throws(() => {
setupClickHandler('nonExistentButton', () => {});
}, /Button with id "nonExistentButton" not found/);
});
it('should handle multiple clicks correctly', () => {
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,以便为我们提供配置的响应。node-fetch 包将简单地替换fetch为浏览器中可用的包。
让我们配置这些包:
import fetch from "node-fetch";
global.fetch = fetch as any;
我们继续看例子:
fetchData.ts
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 error! status: ${response.status}`);
}
return response.json();
}
fetchData.测试.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('should return data when the response is successful', async () => {
const mockData = { message: 'Success' };
nock(baseUrl)
.get('/endpoint')
.reply(200, mockData);
const data = await fetchData(`${baseUrl}/endpoint`);
assert.deepEqual(data, mockData);
});
it('should throw an error when the response is not successful', async () => {
nock(baseUrl)
.get('/endpoint')
.reply(404);
await assert.rejects(
fetchData(`${baseUrl}/endpoint`),
/HTTP error! status: 404/
);
});
it('should handle network errors', async () => {
nock(baseUrl)
.get('/endpoint')
.replyWithError('Network error');
await assert.rejects(
fetchData(`${baseUrl}/endpoint`),
/Network error/
);
});
});
在这里,我们检查我们的函数如何工作,从 API 请求数据。如果我们得到 HTTP 代码 200,那么我们检查一件事,如果有错误,那么再检查另一件事。一般来说,在测试时,最好不要向真实服务器发送请求,因为这不稳定且不可预测,所以最好自己设置以避免出现一堆错误。这样会更快。
另外,我注意到测试本身是重复的,因此您可以将大部分测试移到单独的函数中并在代码中调用它:
函数.ts
const e = (text: string, block: () => unknown, message: string) => {
it(text, () => {
assert.throws(block, {
message
});
});
};
编译.测试.ts
describe("compile function", () => {
e(
"throws an error if the TEMPLATE is not a stringthrows an error if the TEMPLATE is not a string",
() => compile(123 as any),
`${COMPILE_ERROR}: Template was not found or the type of the passed value is not string`
);
这样我们就可以编写更少的代码。
现在我们已经准备好测试,我们需要设置它们的卸载。我们将通过 Codecov 进行此操作
与 Codecov 集成
首先,我们需要有一个存储库。您可以使用不同的服务,但我将在GitHub上向您展示。首先,您需要访问该网站并以方便的方式注册。之后,您将看到一个这样的个人帐户:
在这里,单击配置按钮并按照指南中描述的步骤进行操作。我通过 Github Actions 进行了配置,因此它会自动将报告上传到那里。Github 操作如下所示:
name: CI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
test:
name: Coverage
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% 的测试覆盖率。
如果本文对你有帮助,你可以给项目打一星☆来支持作者。谢谢!
感谢大家阅读这篇文章!
动态图片
【智答专家】您身边免费的GPT4.0人工智能Ai助手,免翻!!!无套路!国内直连,支持文本生成、问答、多语言支持、个性化建议、图片生成、代码纠正等等。