چگونه 100% پوشش کد دریافت کنیم؟ ✅

سلام به همه در این مقاله، من در مورد چگونگی پوشش 100٪ کد برای پروژه خود صحبت خواهم کرد. تکنیک های توضیح داده شده در اینجا به شما این امکان را می دهد که این کار را در سریع ترین زمان ممکن انجام دهید. خوب، بیایید شروع کنیم!
آماده سازی
برای اینکه 100% پوشش داده شود، واضح است که باید آماده شوید. قبل از هر چیز لازم است اجزای زیر شناسایی شوند که در صورت آماده شدن فوراً این فرآیند را سرعت می بخشد:
-
چیزی که ما آزمایش می کنیم: در اینجا ارزش تصمیم گیری در مورد کدی را دارد که در حال آزمایش آن هستیم. این می تواند یک تابع واحد یا یک ماژول جداگانه با مجموعه ای از توابع، حلقه ها و موارد مشابه باشد.
-
به کدام کتابخانه های شخص ثالث نیاز خواهیم داشت: امروزه کتابخانه های زیادی مانند موکا و غیره وجود دارند که به کاربران امکان تست کد را می دهند.
-
از چه فرمتی برای تولید گزارش استفاده کنم: معمولاً برای سرویس هایی مانند Codecov، باید گزارش هایی را در آن تولید کنیم
lcov
قالب
با تصمیم گیری در مورد این در ابتدا، نوشتن تست برای شما آسان تر می شود، زیرا متوجه خواهید شد که این کار را برای چه و برای چه هدفی انجام می دهید.
اکنون ارزش آن را دارد که به بخش عملی آن برویم. در آن، من به شما مثالی میزنم که چگونه آزمایشها را انجام دادم، چگونه میتوانید به این هدف دست پیدا کنید، و به طور کلی چه نکاتی برای این کار در دسترس است.
تمرین کنید
و بنابراین، اول از همه، باید فایلی را که داشتم با پسوند typescript تست کنم. فایل در اینجا قابل مشاهده است:
در مرحله بعد، برای تست این مورد، یک پوشه در ساختار ریشه پروژه ایجاد می کنم. نام دارد test
. در آنجا می توانید اسپلیت های خاصی بدهید، مانند .test.ts
. این همان فایل تایپ اسکریپ است اما با این تفاوت که فقط برای تست در نظر گرفته شده است. گاهی اوقات تست را اضافه نمی کنند، یا مشخصات را اضافه می کنند، اما به هر حال توصیه می کنم فایل هایی با این پسوند ایجاد کنید:
اکنون باید نحوه آزمایش را به طور کلی دریابیم. برای شروع، ما از 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
. وقتی کد خود را تست می کنید، مطمئن شوید که از این دستور استفاده می کنید، زیرا در صورت عدم استفاده از آن، هر بار باید تست را به صورت دستی مجدداً راه اندازی کنید، که به سادگی شما را از آزمایش هر چیزی منصرف می کند.
پس از آن، واضح است که تایپ اسکریپت بدون ماژول های اضافی به جاوا اسکریپت معمولی کامپایل نخواهد شد. برای انجام این کار، باید تعداد بیشتری از این ماژول ها را نصب کنید:
"devDependencies": {
"ts-node": "^10.9.2",
"typescript": "^5.6.3"
}
حالا بیایید مستقیم به خود فایل برویم. فرض کنید همه چیز را تنظیم کرده ایم و می خواهیم برای این تابع یک آزمایش انجام دهیم:
add.test.ts
export function add(a: number, b: number): number {
return a + b;
}
برای این کار موارد زیر را در فایل تست خود می نویسیم:
add.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
روی یک عنصر یا برای بررسی وجود a 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
به طوری که تابع ما در ماژول به جای undefined که در 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);
}
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 = 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 تهیه کنید که پاسخ هایی را که ما پیکربندی می کنیم به ما می دهد. بسته گره واکشی به سادگی جایگزین خواهد شد 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.test.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 را دریافت کنیم، یک چیز را بررسی می کنیم، اگر خطایی وجود دارد، پس از آن چیز دیگری را بررسی می کنیم. به طور کلی، هنگام تست، بهتر است درخواست ها را به سرورهای واقعی ارسال نکنید، زیرا این ناپایدار و غیرقابل پیش بینی است، بنابراین بهتر است برای جلوگیری از انبوهی از خطاها، خود را تنظیم کنید. این به سادگی سریعتر خواهد بود.
همچنین، متوجه شدم که خود تست ها تکرار می شوند، بنابراین می توانید اکثر آنها را به یک تابع جداگانه منتقل کنید و آن را در کد فراخوانی کنید:
functions.ts
const e = (text: string, block: () => unknown, message: string) => {
it(text, () => {
assert.throws(block, {
message
});
});
};
compile.test.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 }}
اکنون، با هر commit، این اکشن اجرا می شود و به طور خودکار بارگذاری می شود. اگر همه چیز به درستی انجام شود، پس می توانید به خود افتخار کنید و یک نشان در README بگذارید که همه چیز تست شده است 🙂
نتیجه گیری
بنابراین، شما قادر خواهید بود همه چیز جالبی را انجام دهید، و چنین توصیه هایی می تواند نه تنها برای جاوا اسکریپت، بلکه برای سایر زبان های برنامه نویسی نیز مناسب باشد، اگر نه برای کتابخانه ها، مطمئناً برای معماری پوشه ها و انتقال به توابع جداگانه. امیدوارم این کار را خوب انجام دهید و 100٪ پوشش آزمایشی برای پروژه های خود داشته باشید.
اگر این مقاله به شما کمک کرد، می توانید با دادن ستاره به پروژه از نویسنده حمایت کنید ☆. متشکرم
از همه شما برای خواندن مقاله متشکرم!