Supertest — HTTP endpoint larni testlash

~20 daq · Express · Testing

Muammodan boshlaylik

Jest bilan funksiyalarni test qila boshladingiz. Yaxshi. Lekin Express da asosiy ish — HTTP so'rovlar. GET /users, POST /auth/login, DELETE /posts/:id.

Bularni qanday testlaysiz? Serverni ishga tushirib, Postman dan qo'lda yuborasizmi? Har o'zgarishda? Bu test emas — bu qiynoq.

Supertest — serverga haqiqiy HTTP so'rov yuborib, javobni tekshirish imkonini beradi. Server ishga tushirilmaydi — to'g'ridan-to'g'ri Express ilovasiga ulanadi.


Supertest nima?

Supertest — Express ilovasini testlash uchun kutubxona. request(app).get('/users') yozasiz — u Express ga HTTP so'rov yuboradi, javobni qaytaradi.

test kodi          Supertest          Express app
    |                  |                   |
    |-- get('/users') ->|                   |
    |                  |-- HTTP so'rov ---->|
    |                  |<-- javob ----------|
    |<-- status, body--|                   |
    |                  |                   |

Real server, real port — kerak emas. Supertest hammasini o'zi boshqaradi.


O'rnatish

npm install --save-dev supertest

Express ilovasini ajratish

Testda app.listen() chaqirilmasligi kerak — Supertest o'zi boshqaradi.

// app.js — faqat Express sozlash
import express from 'express';

const app = express();
app.use(express.json());

app.get('/salom', (req, res) => {
  res.json({ xabar: 'Salom!' });
});

export default app;
// server.js — faqat ishga tushirish
import app from './app.js';

app.listen(3000, () => console.log('Server 3000 portda'));
// server.js ni import qilmang — app.js ni import qiling
import request from 'supertest';
import app from './app.js';

💡 app.js va server.js ni ajratish — test uchun eng muhim qadam. Ko'pchilik bunga e'tibor bermaydi va testda port konflikti yuzaga keladi.


Birinchi test

// tests/app.test.js
import request from 'supertest';
import app from '../app.js';

test('GET /salom — 200 qaytaradi', async () => {
  const res = await request(app).get('/salom');

  expect(res.status).toBe(200);
  expect(res.body.xabar).toBe('Salom!');
});

CRUD testlash

// app.js
const mahsulotlar = [
  { id: 1, nomi: 'Noutbuk', narxi: 5000000 },
  { id: 2, nomi: 'Telefon', narxi: 3000000 },
];

app.get('/mahsulotlar', (req, res) => {
  res.json(mahsulotlar);
});

app.get('/mahsulotlar/:id', (req, res) => {
  const m = mahsulotlar.find((x) => x.id === Number(req.params.id));
  if (!m) return res.status(404).json({ xabar: 'Topilmadi' });
  res.json(m);
});

app.post('/mahsulotlar', (req, res) => {
  const yangi = { id: Date.now(), ...req.body };
  mahsulotlar.push(yangi);
  res.status(201).json(yangi);
});
// tests/mahsulotlar.test.js
import request from 'supertest';
import app from '../app.js';

describe('GET /mahsulotlar', () => {
  test('barcha mahsulotlarni qaytaradi', async () => {
    const res = await request(app).get('/mahsulotlar');

    expect(res.status).toBe(200);
    expect(Array.isArray(res.body)).toBe(true);
    expect(res.body.length).toBeGreaterThan(0);
  });
});

describe('GET /mahsulotlar/:id', () => {
  test('mavjud mahsulotni qaytaradi', async () => {
    const res = await request(app).get('/mahsulotlar/1');

    expect(res.status).toBe(200);
    expect(res.body.nomi).toBe('Noutbuk');
  });

  test('yo\'q mahsulot — 404', async () => {
    const res = await request(app).get('/mahsulotlar/9999');

    expect(res.status).toBe(404);
    expect(res.body.xabar).toBe('Topilmadi');
  });
});

describe('POST /mahsulotlar', () => {
  test('yangi mahsulot qo\'shadi', async () => {
    const res = await request(app)
      .post('/mahsulotlar')
      .send({ nomi: 'Quloqchin', narxi: 500000 });

    expect(res.status).toBe(201);
    expect(res.body.nomi).toBe('Quloqchin');
    expect(res.body.id).toBeDefined();
  });
});

Header va token yuborish

Autentifikatsiya talab qiladigan endpointlarni testlash:

test('autentifikatsiyasiz — 401', async () => {
  const res = await request(app).get('/profil');
  expect(res.status).toBe(401);
});

test('token bilan — 200', async () => {
  const token = jwt.sign({ id: 1 }, process.env.JWT_SECRET);

  const res = await request(app)
    .get('/profil')
    .set('Authorization', `Bearer ${token}`); // ← header qo'shish

  expect(res.status).toBe(200);
  expect(res.body.id).toBe(1);
});

beforeEach / afterEach — har test oldida/keyin

describe('POST /auth/login', () => {
  // Har test oldida DB ni tozalash
  beforeEach(async () => {
    await db.users.destroy({ where: {} });
    await db.users.create({
      email: 'ali@test.com',
      password: await bcrypt.hash('Parol123', 10),
    });
  });

  test('to\'g\'ri ma\'lumotlar — token qaytaradi', async () => {
    const res = await request(app)
      .post('/auth/login')
      .send({ email: 'ali@test.com', password: 'Parol123' });

    expect(res.status).toBe(200);
    expect(res.body.token).toBeDefined();
  });

  test('noto\'g\'ri parol — 401', async () => {
    const res = await request(app)
      .post('/auth/login')
      .send({ email: 'ali@test.com', password: 'xato-parol' });

    expect(res.status).toBe(401);
  });
});

Tez-tez uchraydigan xatolar

1. app.listen() ni app.js da qoldirish

// ❌ — testda port konflikti bo'ladi
const app = express();
// ...
app.listen(3000); // ← app.js ichida

// ✅ — app.js va server.js ajratilgan

2. Test bir-birini buzishi

// ❌ — birinchi test holatni o'zgartiradi, ikkinchiga ta'sir qiladi
test('birinchi', async () => {
  await request(app).post('/mahsulotlar').send({ nomi: 'Test' });
});
test('ikkinchi', async () => {
  const res = await request(app).get('/mahsulotlar');
  expect(res.body.length).toBe(2); // ← birinchi testga bog'liq!
});

// ✅ — beforeEach bilan holatni tiklash

3. Status kodini tekshirmaslik

// ❌ — 404 bo'lsa ham body ni tekshiradi
const res = await request(app).get('/mahsulotlar/999');
expect(res.body.nomi).toBe('Noutbuk'); // ← mantiqsiz

// ✅ — avval status, keyin body
expect(res.status).toBe(404);

Xulosa

  • Supertest — server ishga tushirmasdan Express ga HTTP so'rov yuborish
  • app.js (sozlash) va server.js (ishga tushirish) — ajratish shart
  • .get(), .post(), .send(), .set() — zanjir bilan qulay sintaksis
  • beforeEach — har test oldida holat tozalash
  • Har test: avval status kodi, keyin body

Keyingi dars: 52-dars: Unit Test — service va helper funksiyalarni testlash