Vitest — o'rnatish, birinchi test

~15 daq · React · Testing

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: true bilan describe, it, expect ni har faylda import qilish shart emas. TypeScript da ular uchun turlarni ko'rsatish kerak: tsconfig.json da "types": ["vitest/globals"]. Ba'zi jamoalar aniqlik uchun globals siz, har faylda import { 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] toBe obyektlar uchun havola ni solishtiradi: expect({ a: 1 }).toBe({ a: 1 }) — yiqiladi. Obyekt va massivlar uchun toEqual ishlating.


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.js ni 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 da jest-dom/vitest va cleanup
  • Fayllar: *.test.js kod yonida; vitest — watch, vitest run — CI
  • describe / it; expect(...).toBe, toEqual (obyektlar uchun), toThrow, resolves/rejects
  • vi.fn(), vi.mock(), vi.useFakeTimers() — mock lar; ehtiyotkorlik bilan
  • beforeEach — har test toza holatdan; testlar mustaqil

Keyingi dars

75-dars: React Testing Library — render, screen, userEvent