Unit Test — service va helper funksiyalarni testlash
Muammodan boshlaylik
Supertest bilan HTTP endpointlarni testladingiz. Lekin loyihada ko'p logika endpoint dan tashqarida — service funksiyalarda.
// userService.js
export const foydalanuvchiYarat = async (data) => {
// email mavjudligini tekshirish
// parolni hashlash
// DB ga yozish
// xush kelibsiz emaili yuborish
};
Bu funksiyani Supertest bilan testlash — HTTP dan o'tib, hamma qadam ishlatiladi. Agar email yuborish sinsa — test sinadi, lekin muammo boshqa joyda edi.
Unit test — bitta narsani testlash. Faqat logika, DB va email olmaydi.
Unit test nima?
Unit — eng kichik test birligi. Bitta funksiya, bitta qaror, bitta holat.
Integration test: Unit test:
────────────────── ──────────────────
HTTP so'rov faqat funksiya
→ middleware faqat shu logika
→ route handler tashqi bog'liqlik yo'q
→ service
→ DB
→ email
Unit test tez ishlaydi (millisekundlarda), ko'p holat tekshirish oson.
Hayotiy o'xshatma
Avtomobil zavodi. Integratsion test — tayyor mashinani yo'lga chiqarib sinovdan o'tkazish. Unit test — faqat dvigatelni stendda tekshirish. Dvigatel to'g'ri ishlasa — mashinaga o'rnatiladi.
Har bir qism alohida tekshiriladi. Muammo qayerdaligini aniq biласиз.
Service funksiyasini ajratish
// services/userService.js
import bcrypt from 'bcrypt';
import { db } from '../db.js';
import { emailYuborish } from './emailService.js';
export const foydalanuvchiYarat = async ({ ism, email, parol }) => {
const mavjud = await db.users.findOne({ where: { email } });
if (mavjud) throw new Error('Bu email band');
const hash = await bcrypt.hash(parol, 10);
const user = await db.users.create({ ism, email, parol: hash });
await emailYuborish(email, 'Xush kelibsiz!');
return user;
};
Bu funksiyani unit testlash uchun — db va emailYuborish ni mock qilish kerak. Bu keyingi darsda. Hozir tashqi bog'liqliksiz funksiyalardan boshlaylik.
Sof funksiyalarni testlash
Sof funksiya — faqat kirish ma'lumotiga qarab natija qaytaradi. DB, email, vaqt yo'q.
// utils/helpers.js
export const narxFormat = (narx) => {
return narx.toLocaleString('uz-UZ') + ' so\'m';
};
export const sahifaHisob = (jami, sahifadagi) => {
return Math.ceil(jami / sahifadagi);
};
export const emailValidatsiya = (email) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
// utils/helpers.test.js
import { narxFormat, sahifaHisob, emailValidatsiya } from './helpers.js';
describe('narxFormat', () => {
test('minglikni formatlaydi', () => {
expect(narxFormat(5000000)).toBe('5 000 000 so\'m');
});
});
describe('sahifaHisob', () => {
test('to\'liq bo\'linsa', () => {
expect(sahifaHisob(20, 10)).toBe(2);
});
test('qoldiq bo\'lsa — yuqoriga yaxlitlash', () => {
expect(sahifaHisob(21, 10)).toBe(3);
});
test('sifr element', () => {
expect(sahifaHisob(0, 10)).toBe(0);
});
});
describe('emailValidatsiya', () => {
test('to\'g\'ri email', () => {
expect(emailValidatsiya('ali@example.com')).toBe(true);
});
test('@ yo\'q', () => {
expect(emailValidatsiya('aliexample.com')).toBe(false);
});
test('domen yo\'q', () => {
expect(emailValidatsiya('ali@')).toBe(false);
});
});
Xato holatlari
Funksiya xato tashlasa — uni ham testlash kerak:
// utils/validator.js
export const yoshniTekshir = (yosh) => {
if (typeof yosh !== 'number') throw new TypeError('Yosh raqam bo\'lishi kerak');
if (yosh < 0) throw new RangeError('Yosh manfiy bo\'lishi mumkin emas');
if (yosh > 150) throw new RangeError('Yosh 150 dan katta bo\'lishi mumkin emas');
return true;
};
import { yoshniTekshir } from './validator.js';
describe('yoshniTekshir', () => {
test('to\'g\'ri yosh', () => {
expect(yoshniTekshir(25)).toBe(true);
});
test('raqam emas — TypeError', () => {
expect(() => yoshniTekshir('yigirma besh')).toThrow(TypeError);
});
test('manfiy — RangeError', () => {
expect(() => yoshniTekshir(-1)).toThrow('Yosh manfiy bo\'lishi mumkin emas');
});
test('150 dan katta — xato', () => {
expect(() => yoshniTekshir(200)).toThrow(RangeError);
});
});
💡 Xato testida
expect(() => fn())— funksiyani chaqirib emas, o'rab bering. Aks holda xato test ichida yuz beradi va Jest ushlayolmaydi.
Async funksiyalar
// services/postService.js
export const postniTopish = async (id) => {
const post = await db.posts.findByPk(id);
if (!post) throw new Error('Post topilmadi');
return post;
};
import { postniTopish } from './postService.js';
// Async xato testlash
test('yo\'q post — xato tashlaydi', async () => {
await expect(postniTopish(9999)).rejects.toThrow('Post topilmadi');
});
Har xil holatlarda tekshirish
Yaxshi unit test — chegaraviy qiymatlarni tekshiradi:
describe('sahifaHisob', () => {
test('odatiy holat', () => expect(sahifaHisob(20, 10)).toBe(2));
test('bitta element', () => expect(sahifaHisob(1, 10)).toBe(1));
test('sifr', () => expect(sahifaHisob(0, 10)).toBe(0));
test('sahifadan ko\'p', () => expect(sahifaHisob(100, 10)).toBe(10));
test('qoldiq bor', () => expect(sahifaHisob(11, 10)).toBe(2));
});
Chegaraviy qiymatlar — eng ko'p xato chiqadigan joylar: 0, 1, -1, null, bo'sh massiv.
Tez-tez uchraydigan xatolar
1. Juda katta funksiyani unit testlash
// ❌ — bu unit test emas, integration test
test('foydalanuvchiYarat barcha narsani qiladi', async () => {
// DB, email, bcrypt — hammasi bir testda
});
// ✅ — logikani ajrating, alohida testlang
test('email format to\'g\'ri', () => { ... });
test('parol kamida 8 belgi', () => { ... });
2. Test nomi noaniq
// ❌
test('test 1', () => { ... });
// ✅ — nima tekshirilayotgani aniq
test('manfiy yosh qabul qilinmaydi', () => { ... });
3. Bitta testda bir nechta tekshiruv
// ❌ — qaysi tekshiruv sindi?
test('barcha holat', () => {
expect(fn(1)).toBe(true);
expect(fn(-1)).toBe(false);
expect(fn(0)).toBe(false);
});
// ✅ — har holat alohida test
test('musbat son', () => expect(fn(1)).toBe(true));
test('manfiy son', () => expect(fn(-1)).toBe(false));
test('nol', () => expect(fn(0)).toBe(false));
Xulosa
- Unit test — bitta funksiya, bitta holat, tashqi bog'liqlik yo'q
- Sof funksiyalar — eng oson testlanadi: kirish → kutilgan chiqish
- Xato testida:
expect(() => fn())— funksiyani o'rang - Async xato:
await expect(fn()).rejects.toThrow(...) - Chegaraviy qiymatlarni tekshiring: 0, null, bo'sh, manfiy
Keyingi dars: 53-dars: Integration Test — database bilan testlash