Vitest — o'rnatish, birinchi test
Muammodan boshlaylik
73-darsda testlar nima uchun kerakligini ko'rdik. Endi ularni ishga tushiradigan vosita kerak. Uzoq yillar standart Jest edi. Lekin Vite loyihasida Jest bilan:
- Alohida Babel sozlamasi — Vite o'z konfiguratsiyasiga ega, Jest esa uni tushunmaydi
import.meta.env, alias lar (@/components), CSS importlar — har biri uchun qo'shimcha sozlash- ES modullar bilan muammolar
Vitest — Vite uchun yaratilgan test vositasi. U vite.config.js ni o'zi o'qiydi: alias lar, plaginlar, env — hammasi testlarda ham ishlaydi. API si Jest bilan deyarli bir xil — Jest bilgan har kim darhol ishlay oladi.
O'rnatish
npm install -D vitest jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
| Paket | Vazifasi |
|---|---|
vitest |
Test ishga tushiruvchi |
jsdom |
Node.js da brauzer muhitini taqlid qiladi: document, window |
@testing-library/react |
Komponentlarni render qilish (75-dars) |
@testing-library/jest-dom |
DOM uchun qo'shimcha tekshiruvlar: toBeInTheDocument, toBeDisabled |
@testing-library/user-event |
Foydalanuvchi harakatlari: bosish, yozish |
Sozlash
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom', // brauzer muhiti
globals: true, // describe, it, expect — import siz
setupFiles: './src/test/setup.js', // har test faylidan oldin
},
});
// src/test/setup.js
import '@testing-library/jest-dom/vitest';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';
afterEach(() => {
cleanup(); // har testdan keyin DOM ni tozalash
});
// package.json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"coverage": "vitest run --coverage"
}
}
[!INFO]
globals: truebilandescribe,it,expectni har faylda import qilish shart emas. TypeScript da ular uchun turlarni ko'rsatish kerak:tsconfig.jsonda"types": ["vitest/globals"]. Ba'zi jamoalar aniqlik uchunglobalssiz, har fayldaimport { describe, it, expect } from 'vitest'yozishni afzal ko'radi.
Birinchi test
// src/utils/narx.js
export function formatNarx(son) {
return `${son.toLocaleString('ru-RU').replace(/,/g, ' ')} so'm`;
}
export function chegirmaHisobla(narx, foiz) {
if (narx < 0) throw new Error("Narx manfiy bo'lishi mumkin emas");
const f = Math.min(Math.max(foiz, 0), 100);
return Math.round(narx * (1 - f / 100));
}
Test fayli — yonida, .test.js yoki .spec.js bilan:
// src/utils/narx.test.js
import { describe, it, expect } from 'vitest';
import { chegirmaHisobla } from './narx';
describe('chegirmaHisobla', () => {
it('foiz bo\'yicha chegirma qiladi', () => {
expect(chegirmaHisobla(200_000, 25)).toBe(150_000);
});
it('0% — narx o\'zgarmaydi', () => {
expect(chegirmaHisobla(50_000, 0)).toBe(50_000);
});
it('100% dan ortiq foiz 100% sifatida', () => {
expect(chegirmaHisobla(50_000, 120)).toBe(0);
});
it('manfiy narxda xato', () => {
expect(() => chegirmaHisobla(-1, 10)).toThrow("Narx manfiy");
});
});
npm test
✓ src/utils/narx.test.js (4 tests) 3ms
Test Files 1 passed (1)
Tests 4 passed (4)
Duration 412ms
Watching for file changes...
vitest watch rejimida ishlaydi: faylni saqlasangiz — faqat unga bog'liq testlar darhol qayta ishga tushadi. CI uchun — vitest run (bir marta).
describe, it, test
describe('Savat', () => { // guruh
describe('qoshish', () => { // ichma-ich guruh
it('yangi mahsulotni qo\'shadi', () => {});
it('mavjud mahsulot sonini oshiradi', () => {});
});
it.todo('chegirma kodini qo\'llaydi'); // keyin yoziladi
it.skip('vaqtincha o\'chirilgan', () => {}); // o'tkazib yuborish
it.only('faqat shu ishlaydi', () => {}); // debug uchun — commit qilmang!
});
it va test — bir xil. Test nomini gap sifatida yozing: "Savat qoshish yangi mahsulotni qo'shadi".
expect — asosiy tekshiruvlar
// Tenglik
expect(2 + 2).toBe(4); // === (oddiy qiymatlar)
expect({ a: 1 }).toEqual({ a: 1 }); // chuqur tenglik (obyekt, massiv)
expect(royxat).toStrictEqual([{ id: 1 }]); // undefined xususiyatlar ham
// Haqiqat
expect(qiymat).toBeTruthy();
expect(qiymat).toBeNull();
expect(qiymat).toBeUndefined();
// Sonlar
expect(narx).toBeGreaterThan(0);
expect(0.1 + 0.2).toBeCloseTo(0.3); // kasr sonlar uchun
// Matn va massiv
expect(xabar).toMatch(/xato/i);
expect(royxat).toContain('css');
expect(royxat).toHaveLength(3);
expect(obyekt).toHaveProperty('manzil.shahar', 'Toshkent');
// Xatolar
expect(() => fn()).toThrow('xabar');
await expect(asyncFn()).rejects.toThrow();
await expect(asyncFn()).resolves.toBe(5);
// Inkor
expect(qiymat).not.toBe(0);
[!WARNING]
toBeobyektlar uchun havola ni solishtiradi:expect({ a: 1 }).toBe({ a: 1 })— yiqiladi. Obyekt va massivlar uchuntoEqualishlating.
Mock — soxta funksiyalar
import { vi } from 'vitest';
it('callback ni chaqiradi', () => {
const onSaqla = vi.fn(); // soxta funksiya
formaniYubor({ ism: 'Ali' }, onSaqla);
expect(onSaqla).toHaveBeenCalledTimes(1);
expect(onSaqla).toHaveBeenCalledWith({ ism: 'Ali' });
});
// Qaytaradigan qiymat
const olish = vi.fn().mockReturnValue(42);
const asyncOlish = vi.fn().mockResolvedValue({ id: 1 });
const xatoli = vi.fn().mockRejectedValue(new Error('Tarmoq'));
Modulni soxtalashtirish
import { vi } from 'vitest';
import { buyurtmaBer } from './buyurtma';
import * as api from './api';
vi.mock('./api'); // api modulining barcha eksportlari — vi.fn()
it('buyurtma API ga yuboriladi', async () => {
api.postBuyurtma.mockResolvedValue({ id: 99 });
const natija = await buyurtmaBer([{ id: 1, soni: 2 }]);
expect(api.postBuyurtma).toHaveBeenCalledWith({ mahsulotlar: [{ id: 1, soni: 2 }] });
expect(natija.id).toBe(99);
});
Vaqt
it('3 soniyadan keyin xabar yo\'qoladi', () => {
vi.useFakeTimers();
const yopish = vi.fn();
xabarKorsat('Saqlandi', yopish);
vi.advanceTimersByTime(3000); // 3 soniyani "o'tkazib yuborish"
expect(yopish).toHaveBeenCalled();
vi.useRealTimers();
});
[!TIP] Mock — kuchli, lekin xavfli vosita. Har bir mock — test va haqiqat o'rtasidagi farq. Hammasini soxtalashtirsangiz, test "soxta funksiya soxta funksiyani chaqirdi" ni tekshiradi xolos. API so'rovlari uchun modul mock o'rniga MSW (78-dars) — ancha haqiqatga yaqin.
Setup va teardown
describe('Savat store', () => {
beforeEach(() => {
useSavatStore.setState({ mahsulotlar: [] }); // har testdan oldin — toza holat
});
afterEach(() => {
vi.restoreAllMocks();
});
it('...', () => {});
});
| Funksiya | Qachon |
|---|---|
beforeEach / afterEach |
Har bir testdan oldin / keyin |
beforeAll / afterAll |
Guruhdagi barcha testlardan oldin / keyin bir marta |
Har bir test mustaqil bo'lishi kerak: boshqa testlar natijasiga bog'liq bo'lmasin va tartibi muhim bo'lmasin.
Coverage va UI
npm install -D @vitest/coverage-v8 @vitest/ui
npx vitest run --coverage # qaysi qatorlar tekshirilmagan — hisobot
npx vitest --ui # brauzerda chiroyli interfeys
Nima uchun shunday?
Nima uchun Vitest Jest dan tezroq?
Asosiy sabab — u Vite ning o'zgarishlar grafigini ishlatadi. Vite allaqachon qaysi fayl qaysi faylni import qilishini biladi (HMR — "issiq almashtirish" uchun). Vitest shu bilimdan foydalanadi: siz narx.js ni o'zgartirsangiz, u faqat narx.js ni import qiladigan testlarni qayta ishga tushiradi — 500 ta testning hammasini emas. Bundan tashqari, kodni o'zgartirish (JSX, TypeScript) uchun Vite ning tez transformatsiya vositalari ishlatiladi va testlar parallel oqimlarda bajariladi. Natijada watch rejimida test natijasi faylni saqlashingiz bilan deyarli bir zumda chiqadi — va bu testlarni yozishni yoqimli qiladi.
Xulosa
Eslab qoling:
- Vitest — Vite uchun test vositasi;
vite.config.jsni o'qiydi; API Jest bilan bir xil - O'rnatish:
vitest,jsdom,@testing-library/react,jest-dom,user-event test: { environment: 'jsdom', globals: true, setupFiles }; setup dajest-dom/vitestvacleanup- Fayllar:
*.test.jskod yonida;vitest— watch,vitest run— CI describe/it;expect(...).toBe,toEqual(obyektlar uchun),toThrow,resolves/rejectsvi.fn(),vi.mock(),vi.useFakeTimers()— mock lar; ehtiyotkorlik bilanbeforeEach— har test toza holatdan; testlar mustaqil
Keyingi dars
75-dars: React Testing Library — render, screen, userEvent