Mock — funksiya, modul, database mock qilish
Muammodan boshlaylik
foydalanuvchiYarat funksiyasini unit testlamoqchisiz. Lekin u DB ga yozadi va email yuboradi.
Unit testda DB va email bo'lmaydi — ular tashqi tizimlar. Test uchun real DB kerak bo'lmasligi kerak. Email yuborilmasligi kerak.
Lekin funksiya ichida DB va email bor. Qanday testlaysiz?
Mock nima?
Mock — haqiqiy funksiya o'rniga qo'yiladigan "soxta" funksiya. Haqiqiy ishni bajarmasligi — faqat "ha, chaqirildi, mana natija" deyishi kerak.
Haqiqiy: Mock:
───────────────── ─────────────────
emailYuborish() emailYuborish()
→ SMTP ga ulanish → "yuborildi" deydi
→ email yuborish → hech narsa qilmaydi
→ 3 sekund kutish → 0ms
Hayotiy o'xshatma
Aktyorlar uchun "dublyaj". Film tushirishda asl uchuvchi kerak emas — dublyyor uchadi. Asosiy aktyor "uchayapti" deb o'ynaydi. Test ham shunday: asosiy funksiya email "yubordi" deb hisoblaydi, aslida hech narsa ketmaydi.
jest.fn() — oddiy mock
const emailYuborish = jest.fn(); // ← soxta funksiya
emailYuborish('ali@test.com', 'Salom');
expect(emailYuborish).toHaveBeenCalled(); // chaqirildimi?
expect(emailYuborish).toHaveBeenCalledTimes(1); // necha marta?
expect(emailYuborish).toHaveBeenCalledWith('ali@test.com', 'Salom'); // nima bilan?
jest.mock() — modulni mock qilish
Modul import qilingan bo'lsa — jest.mock() bilan almashtirish:
// services/userService.js
import { emailYuborish } from './emailService.js';
import bcrypt from 'bcrypt';
import { db } from '../db.js';
export const foydalanuvchiYarat = async ({ ism, email, parol }) => {
const hash = await bcrypt.hash(parol, 10);
const user = await db.users.create({ ism, email, parol: hash });
await emailYuborish(email, 'Xush kelibsiz!');
return user;
};
// services/userService.test.js
import { foydalanuvchiYarat } from './userService.js';
// Modullarni mock qilish
jest.mock('./emailService.js');
jest.mock('../db.js');
import { emailYuborish } from './emailService.js';
import { db } from '../db.js';
test('foydalanuvchi yaratildi va email yuborildi', async () => {
// DB mock — ma'lumot qaytaradi
db.users.create.mockResolvedValue({
id: 1, ism: 'Ali', email: 'ali@test.com',
});
const user = await foydalanuvchiYarat({
ism: 'Ali', email: 'ali@test.com', parol: 'Parol123',
});
expect(user.id).toBe(1);
expect(emailYuborish).toHaveBeenCalledWith('ali@test.com', 'Xush kelibsiz!');
});
mockResolvedValue va mockReturnValue
// Oddiy qiymat qaytarish
fn.mockReturnValue(42);
fn(); // → 42
// Promise qaytarish (async funksiya)
fn.mockResolvedValue({ id: 1, ism: 'Ali' });
await fn(); // → { id: 1, ism: 'Ali' }
// Xato tashlash
fn.mockRejectedValue(new Error('Topilmadi'));
await fn(); // → Error tashlaydi
Amaliy misol — to'liq service testi
// services/postService.js
import { db } from '../db.js';
import { emailYuborish } from './emailService.js';
export const postYarat = async ({ sarlavha, matn, userId }) => {
const user = await db.users.findByPk(userId);
if (!user) throw new Error('Foydalanuvchi topilmadi');
const post = await db.posts.create({ sarlavha, matn, userId });
await emailYuborish(user.email, `Postingiz "${sarlavha}" e'lon qilindi`);
return post;
};
// services/postService.test.js
import { postYarat } from './postService.js';
jest.mock('../db.js');
jest.mock('./emailService.js');
import { db } from '../db.js';
import { emailYuborish } from './emailService.js';
describe('postYarat', () => {
test('post yaratadi va email yuboradi', async () => {
db.users.findByPk.mockResolvedValue({ id: 1, email: 'ali@test.com' });
db.posts.create.mockResolvedValue({ id: 10, sarlavha: 'Salom dunyo' });
const post = await postYarat({
sarlavha: 'Salom dunyo', matn: 'Matn...', userId: 1,
});
expect(post.id).toBe(10);
expect(emailYuborish).toHaveBeenCalledWith(
'ali@test.com',
'Postingiz "Salom dunyo" e\'lon qilindi',
);
});
test('foydalanuvchi topilmasa — xato', async () => {
db.users.findByPk.mockResolvedValue(null); // ← foydalanuvchi yo'q
await expect(
postYarat({ sarlavha: 'Test', matn: '...', userId: 999 })
).rejects.toThrow('Foydalanuvchi topilmadi');
expect(emailYuborish).not.toHaveBeenCalled(); // ← email yuborilmagan
});
});
beforeEach da mock tozalash
Bir test mockni o'zgartirsa — keyingi test ham ta'sirlanaoladi:
beforeEach(() => {
jest.clearAllMocks(); // ← har testdan oldin mockni tozalash
});
Tez-tez uchraydigan xatolar
1. Mock o'rniga real funksiya chaqirilishi
// ❌ — jest.mock import dan keyin yozilgan
import { emailYuborish } from './emailService.js';
jest.mock('./emailService.js'); // ← kech! Import allaqachon bo'ldi
// ✅ — jest.mock birinchi (Jest uni hoistlab tepaga ko'chiradi)
jest.mock('./emailService.js');
import { emailYuborish } from './emailService.js';
2. Mock qaytarmaslik
// ❌ — undefined qaytaradi
db.users.findByPk.mockResolvedValue(); // ← qiymat yo'q
// ✅
db.users.findByPk.mockResolvedValue({ id: 1, email: 'ali@test.com' });
3. Mock tozalanmasa
// ❌ — birinchi test mockni sozladi, ikkinchi ham shuni ishlatadi
test('birinchi', () => {
db.users.findByPk.mockResolvedValue({ id: 1 });
});
test('ikkinchi', () => {
// db.users.findByPk hali ham { id: 1 } qaytaradi!
});
// ✅
beforeEach(() => jest.clearAllMocks());
Xulosa
- Mock — tashqi tizim (DB, email, API) o'rnidagi soxta funksiya
jest.fn()— oddiy mock;jest.mock('modul')— butun modulni almashtirishmockResolvedValue— async mock;mockRejectedValue— xato mocktoHaveBeenCalled,toHaveBeenCalledWith— mock chaqirildimi tekshirishbeforeEach(() => jest.clearAllMocks())— testlar orasida mock tozalash
Keyingi dars: 55-dars: Test Coverage — Istanbul bilan