Тэставанне модуляў на JavaScript за дапамогай Jest і Vitest: практыкі, якія застаюцца эфектывнымі
Як правільна перацовка тэстаў на JavaScript: што належыць да ўзелковага слоя, Vitest проты Jest проты Mocha, падход arrange-act-assert, мокі, крайнія кейсы, CI, і дзе дапамага AI.
Крыніцы: JavaScript Unit Testing Best Practices: A Practical Guide with Jest and Vitest Examples
Тэсты на адзінкі выдзеляюць самыя маленькіе элементы, якія можна працаваць — функцыі, методы, класы — і пераканальваюцца, што яны правільна працуюць. Яны знаходзяцца у основе піраміды тэставання, таму яны должны адражаць рэальную бізнес-логіку і рэальныя рызыкі, а не тэрмінологію.
Апоўнаючыся практыкамі промышленасці, тут прэдставлена практычная аднарада па эфектывнам тэставанні на адзінкі ў JavaScript.
Што робіць тэст на адзінкі хорашым?
Хорашыя тэсты на адзінкі пераканальваюцца рэзультатамі, а не прыватнымі способамі рэалізацыі. Гэта дапамагае захаваць стабільнасць набору тэстаў пад час рэфакторавання внутрошняй часты коду.
Характэрыстыкі, якія павтараюцца ўсё часта:
- Шырокі — за мілісекунды. Медленныя наборы тэстаў не запускаюцца пры зберэжэнні змян.
- Ізольаваны — без залежнасці ад стану паказвальных тэстаў; можна безбедна перасортаваць іх адночасова.
- Дэтэрміністычны — адносна тое ж рэзультата ў кожны раз, колі яго запускаюць. Рэзультат «локальна успех» / «у CI неудача» означае ненадзейнасць тэста, а такія тэсты разрушаюць довер'е.
Прыклад структуры:
// A clean, predictable unit test using Jest/Vitest syntax
describe('calculateTotalWithTax', () => {
it('should apply a standard 10% tax rate to the subtotal', () => {
// Arrange
const subtotal = 100;
const taxRate = 0.10;
// Act
const result = calculateTotalWithTax(subtotal, taxRate);
// Assert
expect(result).toBe(110);
});
it('should throw an explicit error if the subtotal is negative', () => {
expect(() => calculateTotalWithTax(-50, 0.10)).toThrow('Invalid subtotal');
});
it('should return the subtotal unchanged when tax rate is 0', () => {
// Edge case: no tax applied
expect(calculateTotalWithTax(100, 0)).toBe(100);
});
it('should round to 2 decimal places to avoid floating-point errors', () => {
// Guards against bugs like 19.99 * 1.10 = 21.989000000000004
expect(calculateTotalWithTax(19.99, 0.10)).toBe(21.99);
});
});
Што належыць (і што не належыць)
Зберагаюце ўніт-тэсты чыстымі, так каб цяплая стрэлка означала паўстанак бізнес-логікі. Усе іншае належыць да вышэйшых роўней піраміды.
Структура піраміды: многа быстрых уніт-тэстоў у ніжней частцы, менша колькасць інтэграцыйных тэстоў у серэдзіне, тонкі слой E2E на верхней частцы. Якшо перакрысіце ўсё гэта, вы отрымаеце медленныя, ненадзейныя, дорогія наборы тэстаў.
Што належыць у ўніт-тэстах
Няма I/O, няма побачных эфектаў, няма залежнасцей, якімі вы не керуеце:
- Чыстыя функціі (калькуляторы податкаў, фарматары, парсеры дат, механізмы зніжак)
- Трансформацыі (map/filter/sort, API → view-model)
- Бізнес-правіла (валідацыя, рангі цэнаў, апштрыкты праваў) — крайнія прыклады знаходзяцца тут, а ўніт-тэсты являюцься дыяўергентным рашэнням
Калі вы можэце сказаць «з урахованнем гэтых вводных, магчыма ажыцьцеверыць гэтыя выходныя даныя» без сеткі, файловай сістэмы чы працюючага сервісу, то гэта ўнітарны тэст. Расглядзіце всі крайнія случаў — галоўная мета ў тым, што такія тэсты выконваюцца быстра.
Чаго утрымвацься
Команды марнуюць час на ўнітарныя тэсты неправільных слоёў:
- Бібліятэкі трохцяхоў (Lodash sort, Axios headers) — вы не являецеся ўсім іх CI
- З’яўленні/запыты да базы дадзенаў — нават варыянт у памяці SQLite ўжо належыць да інтэграцыйных тэстаў
- Складныя сеткавыя раштры (MSW + павторныя спробы + перакананні ў заголовках) — тэрыторыя інтэграцыйных тэстаў
- Ўсе, што трэбуе Docker, ачыненых портав чы запуску compose спачатку
Тэст на інтуіцыю: ад’екруйце інтэрнэт. Калі тэст не працюе, гэта не ўнітарны тэст.
Выбор фрэймворку JavaScript
Падабейце інструмент да практыку будавання; аптымізавайце для досвяду распрацоўшчыка, а не толькі для моднасці. Адаптаванне пад нізькі настройкі лепшае, чым падбор найновейшага інструменту.
Для большай часткі сучасных прыемакоў выбор стаў Vitest проты Jest.
Vitest — моцны стандарт з Vite
Ён пабудаваны на Vite, перадае настройкі і падтрымляе TypeScript і натыўны ESM. Рэжым абзірвання і выконанне зазвычай быстрэйшыя, чым у старэйшых інструментаў, а API, сэродзеўны Jest, спрыяе легкай міграцыі.
Jest — стандарт промышленасці для багато практык React
У Webpack, Next.js або старэйшых структурах у стылі CRA, Jest ўсеўключаючы рашэння: асерты, мокі, кантроль паверхні тэстаў, зьліквы. У вялічзіных рэпазітарыях ён моцнейшы, а ESM у яму менш зручны, чым у Vitest, але ўжо завершанасць і екосыстэма заставляют яго выграваць для багато команд.
Mocha — модульныя структуры
Корыстны для бэкендаў на Node, калі хочаце самі сабраць Chai + Sinon. Больш наяўных настройкаў, больш кантролю.
Кароткі выбор
- Праекты на Vite → Vitest
- React з Webpack / Next / CRA → Jest
- Node API, які выкарыстоўваюць модульную структуру → Mocha
Практыкі (у стыле Jest/Vitest)
Старацца пра тэсты, якія ўжывач можа зрозумець, якія ёсць дэтэрміністычныя і якія можна без проблем перэрабіць.
1. Arrange–Act–Assert
Заўжды адной і той жа структура:
import { describe, it, expect } from 'vitest';
import { add } from './math';
describe('add()', () => {
it('returns the sum of two numbers', () => {
// Arrange
const a = 2;
const b = 3;
// Act
const result = add(a, b);
// Assert
expect(result).toBe(5);
});
});
2. Назвы, якія опісваюць поведзенне
Убудованыя элементы у стыле BDD з describe / it должны чытацца як рэчанкі:
describe('CartService', () => {
describe('when adding an item', () => {
it('increases the total cart quantity by one', () => {
// Test code...
});
it('updates the final checkout price', () => {
// Test code...
});
});
});
Засоб для запуску тэста выдрукоўвае чытабельную рэчанку, якая паводзіцься пра службу, сітуацыю і апэктыўную змяну колькасці.
3. Аднародныя фікстуры, а не розбросаныя літэралы
const testUser = {
name: 'Alice',
age: 30,
};
it('returns the user name', () => {
expect(getUserName(testUser)).toBe('Alice');
});
Змяніце прыкладныя даныя толькі ў аднам месцы.
4. Паведзенне, а не рэалізацыя
Пераканваюцца ў рэзультатах выходу, а не у тым, які прыватны функцыя выканана. Тэсты, звязаныя з рэалізацыёю, ламаюцца пад час безнэшкодных перэрабоц.
5. Акуратна імітавацыя зовнішніх элементаў
Стабы API/БД/сторонніх аплікацый для прыткасці і непаўнаважальнасці. Двойкі:
- Стабы кантролююць вхідныя даны
- Шпіёны сочаць за взаімадзеямі
- Мокі, калі взаімадзея ёсць самай асэртывам
- Фейкі як маленькія рабочыя заменнікі
Стабаванне fetch:
import { vi } from 'vitest';
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ name: 'Alice' }),
})
);
Не стварайце мокіў для сябе ж бізнес-логікі, як толькі не ўтрэба — чыразмокаванне пакрывае дыфекты.
6. Крайнія кейсы і адказы
Граніцы, null-значэння, порожнія строкі, некарэктныя аргументы. Абдзержайце мінімум/максімум/трохі больш, порожнія колекцыі, відсутныя поля, неправыя тыпы, ад’юбатныя значэння, крайнія цэлыя чысла.
7. Незалежнасць
Будзь-які парадак, будзь-якае паралельнае планаванне. Аднародныя змінныя фікстуры ствараюць каскады і неперапрацоўванне. Лепш выкарыстоўваць beforeEach для чыстага стану.
8. CI на кожным PR
Заўядзіце набор тэстаў на елементарны ранг у сістэме цяплай інтэграцыі, каб рэгрэсіі не дазволілі здзейсніць з’едыненне, а праўяжаныя тэсты не маглі ўсё пакрыць.
Роль AI — і як застаўляюцься людзі
Мадэлі большых языков ускорвают стварэнне шаблонных, павторных варыянтов і прыкладных тэмплейтаў. Заставляючы мадэль працаваць з чыстымі функцыямі і крэтэрыямі адпраўнення, можна стварыць шырокі набор рашэнняў, замаскаваць складныя аспекты і сінтэтычныя даныя, якія людзі працягваюць ігнараваць пад тэрміном.
Мадэлі яшчэ не маюць архітэктурнага і доменнага суджэння. Корыстны падход: дазвольце AI стварыць вялікі спектр можлівых сцэнарыёў, а потым перагляньце кожную тэзу ў супрацоўке з рэальнымі трэбаваннямі пры з’еднанні. Вы стаеце рэдактаром „правільных“ рашэнняў; мадэль працюе быстра.
Вашы сасавы з тэставання на елементарным рывені дапаможуць ўточніць гэта ўсё больш — спрыймайце вышэўказаныя практыкі як базовую лінію, а не як готавы спіс пераканаў.
Стварэнне пачатковага набора без перавантажэння
Пачніце з чыстых функцый домэна: вычыслення грошаў, перакананне ў падпярэнні, парсеры. Запішыце тэсты AAA з назвамі поведэння і спяльнымі фікстурамі. Дадзіце краявыя кейсы, пакуль матрыца “null / empty / max” не стане нудной. Ляч тады прыкладзіцеся да компонентаў UI, і толькі для поведэння prop→render, якое не патрабуе браузера.
Калі тэст патрабуе базу дадзеных або Docker, намеравана пераказціце яго ў папку інтэграцыі. Строгае сачаванне папкі з тэстамі елементаў захоўвае кантракт, па якому цяжка-чырвонае стаўкі ўказвае на парадукс у логіцы.
Пры выборэ фрэймворку не мяніце Jest на Vitest серэд розрабоцы новых функцый проста з меты моднай адміністрацыі. Калі вы ужо вікорыстоўвайце Vite, Vitest — гэта натуральны выбар. Калі монарэпа заснована на Jest і выкарыстоўвае стандарты Next.js, застаіцеся і інвестуйце ў прыгонкі в іншых аспектах (распад CI, адсортаванне снімакаў).
Рытуалы адзінаковага перагляду
Пад час перагляду PR адхарацьце тэсты, якія пераканаліваюцься ў лічбу вызоў прыватных методаў. Адхарацьце спяльныя, зменныя фікстуры між файламі. Першы чым з’еднаць код, неабходна, каб тэсты на елементарныя функціі давалі рэзультат «зелены» у CI. Перыядычна выдаліце тэсты, якія проста павтараюць поведзенне бібліятэкі для мокінгу.
Калі вы вікорыстоўваеце AI для стварэння набораў тэстаў, запішыце чыстую функцію і крытэрыя прыйнятнасі, а потым выдаліце пераканаленні, якія маюць у сабе выдуманыя вакуфікацыі. Людзі яшчэ і так вяршаюць, што значыць «правільна»; мадэлі проста пішуць быстрэй.
Памятка напрыканце
Елементарныя тэсты ўтвараюць недорогую страховку, калі яны застаюцца шырокімі, ізольаванымі і сфокусаванымі на рэзультатах. Захавайце гэты нішовы падход. Дазвольце інтэграцыйным тэстам і тэстам E2E кераваць рэштой структуры, ўжо таму, каб база ніколі не стала мініяцюрным прыемным сераўерам.
Каталог конкрэтных крайніх прыкладаў, якія можна скопіюваць
Для чысловых API: ноль, адзін, максімальна значэнне целага, адваротныя значэнні, NaN, як гэта прыменяецца, а таксама значэнні на адзін больш за задокументаваны ліміт. Для стрэнгаў: порожній, толькі прабелы, Unicode, чрэзвычайна дугі. Для калекцый: порожняя, з адным элементам, дуплікаты, спакаяныя або зворачана спакаяныя. Для об’ектаў: відсутныя ключы, null-значэнні ў вёскапаных полях, нечаканыя дадатковыя ключы, калі ваш парсер ўскладнены.
Запішыце іх аднойчы як прыклады, базаваныя на табліце, калі інструмент для запуску гэта падтрымляе. Такі каталог становіцца частынай культуры команды і запобiegае прагласам, якія адносуюцца толькі да “гарных” сцэнарыяў.
Снімкі стану з абераганнем
Снімкі стану у Jest падыходзяць для стабільнага візуальнага выхаду, але ўпадзячы для маркапаў, якія зменяюцца. Для інтэрактыўных компанентаў лепш выкарыстоваць явныя асэрцыі ўзгалі і на тэкст, за дапамогою Testing Library. Калі вы застаўляеце снімкі стану, перагледзіце разлікі як код для працы ў продакшэне — а не як шум, які трэба проста пераклікнуць.
Паралельныя падказкі для CI
Разбіляйце тэсты па файлах, негайна адзначайце абяроны ў першым тэсце на едыніцу, і старайцеся, каб час выканання тэста на едыніцу не перавышаў калькі хвілін. Якшча працэс CI для тэстаў на едыніцу рэгулярна перавышае гэты час, значыць у вас або занадта многі тэсты інтэграцыі, якія пазначаны як тэсты на едыніцу, або ў файле setupFiles знаходзіцца код для запуску асяроддзя, які спамошчае працэс.
Аддзельна сачыце за канцэнтрацыяй абяронаў. Суіта тэстаў, якая становіць «зелёную» пасля двух перапрыяў, не ёсць «зелёной».