19-bo‘lim
Testlar va nosozliklar
node:test bilan test yozish, assert, describe va it, Express ilovasini sinash, qamrov va nosozlikni izlash usullari.
Ushbu bo‘lim mundarijasi
Node 18 dan beri test yurituvchi tilning ichida bor. jest yoki
mocha o'rnatish shart emas.
Birinchi test #
Testlar odatda *.test.mjs fayllarida yoziladi:
import { test } from "node:test";
import assert from "node:assert/strict";
test("qo'shish to'g'ri ishlaydi", () => {
assert.equal(2 + 2, 4);
});
Ishga tushirish:
node --test
Natija taxminan shunday bo'ladi:
✔ qo'shish to'g'ri ishlaydi (1.2ms)
ℹ tests 1
ℹ pass 1
ℹ fail 0
assert moduli #
import assert from "node:assert/strict";
assert.equal(2 + 2, 4);
assert.deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] });
assert.ok([1, 2, 3].includes(2));
assert.match("salom dunyo", /dunyo/);
assert.throws(
() => { throw new TypeError("noto'g'ri tur"); },
TypeError,
);
console.log("Hamma tasdiq o'tdi");
try {
assert.equal(1, 2);
} catch (xato) {
console.log("Xato nomi:", xato.name);
console.log("Kutilgan:", xato.expected, "haqiqiy:", xato.actual);
}
Hamma tasdiq o'tdi
Xato nomi: AssertionError
Kutilgan: 2 haqiqiy: 1
| Tasdiq | Nimani tekshiradi |
|---|---|
assert.equal | Qiymatlar teng (===) |
assert.deepEqual | Obyekt/massiv ichma-ich teng |
assert.ok | Qiymat rost |
assert.match | Satr naqshga mos |
assert.throws | Funksiya xato tashlaydi |
assert.rejects | Vada rad etiladi |
node:assert ikki shaklda keladi:
import assert from "node:assert"; // bo'sh (==)
import assert from "node:assert/strict"; // qat'iy (===)
Birinchisida assert.equal("2", 2) o'tib ketadi, chunki u
== bilan solishtiradi. Bu testni yolg'on ishonch beradigan
qiladi.
/strict esa === ishlatadi va bunday holatni ushlaydi. Yangi
kodda har doim shu.
Testlarni haqiqatan ishga tushirish #
import { run } from "node:test";
import { writeFile, rm } from "node:fs/promises";
await writeFile("hisob.test.mjs", `
import { test } from "node:test";
import assert from "node:assert/strict";
function qosh(a, b) { return a + b; }
test("musbat sonlar", () => {
assert.equal(qosh(2, 3), 5);
});
test("manfiy sonlar", () => {
assert.equal(qosh(-2, -3), -5);
});
test("ataylab yiqiladi", () => {
assert.equal(qosh(2, 2), 5);
});
`);
const natijalar = [];
for await (const hodisa of run({ files: ["hisob.test.mjs"] })) {
if (hodisa.type === "test:pass") {
natijalar.push(`${hodisa.data.name}: o'tdi`);
}
if (hodisa.type === "test:fail") {
natijalar.push(`${hodisa.data.name}: yiqildi`);
}
}
console.log(natijalar.join("\n"));
await rm("hisob.test.mjs");
musbat sonlar: o'tdi
manfiy sonlar: o'tdi
ataylab yiqiladi: yiqildi
run() - test yurituvchining dasturiy shakli. U hodisalar oqimini
qaytaradi.
Kundalik ishda esa node --test buyrug'i ishlatiladi.
describe va it #
import { describe, it } from "node:test";
import assert from "node:assert/strict";
function kesim(ball) {
if (ball >= 90) return "A";
if (ball >= 75) return "B";
return "C";
}
describe("kesim funksiyasi", () => {
it("90 va undan yuqori uchun A qaytaradi", () => {
assert.equal(kesim(95), "A");
assert.equal(kesim(90), "A");
});
it("75 dan 89 gacha B qaytaradi", () => {
assert.equal(kesim(80), "B");
});
it("qolganlari uchun C", () => {
assert.equal(kesim(50), "C");
});
});
node --test uni shunday ko'rsatadi:
▶ kesim funksiyasi
✔ 90 va undan yuqori uchun A qaytaradi
✔ 75 dan 89 gacha B qaytaradi
✔ qolganlari uchun C
✔ kesim funksiyasi
ℹ tests 3
ℹ pass 3
ℹ fail 0
describe testlarni guruhlaydi, it esa bitta holatni tasvirlaydi.
test() bilan farqi yo'q - bu shunchaki boshqa uslub. jest yoki
mocha dan kelganlarga describe/it tanishroq.
Yaxshi test nomi nima kutilayotganini aytadi:
it("90 va undan yuqori uchun A qaytaradi", ...)
Yomon nomlar: it("test 1"), it("kesim"), it("ishlaydi").
Sababi amaliy: test yiqilganda siz faqat nomni ko'rasiz. Yaxshi nom nima buzilganini darhol aytadi, yomoni esa kodga qarashga majbur qiladi.
Express ilovasini sinash #
import express from "express";
export function ilovaYarat() {
const app = express();
app.use(express.json());
const talabalar = new Map([[1, { id: 1, ism: "Malika" }]]);
app.get("/talabalar/:id", (sorov, javob) => {
const talaba = talabalar.get(Number(sorov.params.id));
if (!talaba) {
javob.status(404).json({ xato: "topilmadi" });
return;
}
javob.json({ malumot: talaba });
});
app.post("/talabalar", (sorov, javob) => {
if (!sorov.body.ism) {
javob.status(422).json({ xato: "ism kerak" });
return;
}
const id = talabalar.size + 1;
talabalar.set(id, { id, ism: sorov.body.ism });
javob.status(201).json({ malumot: { id, ism: sorov.body.ism } });
});
return app;
}
import { run } from "node:test";
import { writeFile, rm } from "node:fs/promises";
await writeFile("api.test.mjs", `
import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { ilovaYarat } from "./sinov-ilova.mjs";
let server;
let asos;
before(() => {
server = ilovaYarat().listen(0);
asos = "http://localhost:" + server.address().port;
});
after(() => server.close());
test("mavjud talabani qaytaradi", async () => {
const javob = await fetch(asos + "/talabalar/1");
assert.equal(javob.status, 200);
const tana = await javob.json();
assert.equal(tana.malumot.ism, "Malika");
});
test("yo'q talaba uchun 404", async () => {
const javob = await fetch(asos + "/talabalar/999");
assert.equal(javob.status, 404);
});
test("ismsiz so'rovni rad etadi", async () => {
const javob = await fetch(asos + "/talabalar", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
assert.equal(javob.status, 422);
});
`);
let otdi = 0;
let yiqildi = 0;
for await (const hodisa of run({ files: ["api.test.mjs"] })) {
if (hodisa.type === "test:pass") otdi++;
if (hodisa.type === "test:fail") yiqildi++;
}
console.log("O'tdi:", otdi, "| Yiqildi:", yiqildi);
await rm("api.test.mjs");
O'tdi: 3 | Yiqildi: 0
Mana 11-bo'limda ilovani alohida faylga ajratishning sababi:
ilovaYarat() ni test ichidan chaqirib, unga to'g'ridan-to'g'ri
so'rov yuborish mumkin.
| Ilgak | Qachon ishlaydi |
|---|---|
before | Hamma testdan oldin bir marta |
after | Hamma testdan keyin bir marta |
beforeEach | Har bir testdan oldin |
afterEach | Har bir testdan keyin |
Testlar mustaqil bo'lsin #
Yuqoridagi misolda nozik muammo bor: POST testi yangi talaba
qo'shadi va u keyingi testlarga ko'rinadi.
Hozircha zarar yo'q, lekin testlar soni o'sgani sari bu o'zaro ta'sir paydo qiladi: test alohida o'tadi, birga yiqiladi. Yoki tartib o'zgarsa buziladi.
To'g'ri yondashuv - beforeEach bilan har bir testga toza
holat berish:
let server;
beforeEach(() => { server = ilovaYarat().listen(0); });
afterEach(() => server.close());
Har bir test o'z ilovasini oladi. Sekinroq, lekin ishonchli.
Qoida: test istalgan tartibda, yakka o'zi ham o'tishi kerak.
Qamrov #
node --test --experimental-test-coverage
Qamrov kodning necha foizi testlar bilan qamralganini ko'rsatadi.
Qamrov foydali, lekin u faqat qaysi qatorlar ishga tushganini o'lchaydi - ular to'g'ri tekshirilganini emas.
Bu test 100% qamrov beradi va hech narsani tekshirmaydi:
test("yomon test", () => {
kesim(95); // chaqirildi, lekin natija tekshirilmadi
});
Foydaliroq savol: "qaysi holatlar sinalmagan?" Chegaraviy qiymatlar, bo'sh kiritma, xato yo'llari.
Qamrov past bo'lsa - bu haqiqiy signal. Yuqori bo'lsa - bu hali hech narsani isbotlamaydi.
Nosozlikni izlash #
function ortacha(sonlar) {
const jami = sonlar.reduce((a, b) => a + b, 0);
return jami / sonlar.length;
}
console.log("Oddiy:", ortacha([10, 20, 30]));
console.log("Bo'sh massiv:", ortacha([]));
console.log("NaN mi:", Number.isNaN(ortacha([])));
console.table([
{ ism: "Malika", ball: 88 },
{ ism: "Husanboy", ball: 92 },
]);
Oddiy: 20
Bo'sh massiv: NaN
NaN mi: true
┌─────────┬────────────┬──────┐
│ (index) │ ism │ ball │
├─────────┼────────────┼──────┤
│ 0 │ 'Malika' │ 88 │
│ 1 │ 'Husanboy' │ 92 │
└─────────┴────────────┴──────┘
console.table bir xil shakldagi obyektlar ro'yxatini jadval
qilib chizadi. Ro'yxatni tekshirganda console.log dan ancha
o'qishliroq.
| Vosita | Qachon |
|---|---|
console.log | Tez tekshiruv |
console.dir(x, { depth: null }) | Chuqur obyekt (15-bo'lim) |
console.table | Bir xil shakldagi qatorlar |
node --inspect | Haqiqiy nosozlik izlagich |
console.log bilan izlash tez, lekin chegarasi bor: har safar
kodni o'zgartirib, qayta ishga tushirish kerak.
Haqiqiy nosozlik izlagich:
node --inspect-brk index.js
So'ng Chrome da chrome://inspect ni oching yoki muharriringizni
ulang (VS Code da tayyor sozlama bor).
U beradi: to'xtash nuqtalari, har bir o'zgaruvchining qiymati, chaqiruvlar steki, qadamba-qadam yurish.
--inspect-brk birinchi qatorda to'xtaydi - dastur tez
tugaydigan bo'lsa, aynan shu kerak.
Qanday test yozish kerak #
Har bir funksiya uchun:
- Oddiy holat - hammasi to'g'ri bo'lganda nima qaytaradi?
- Chegara - bo'sh massiv, nol, eng katta qiymat,
null? - Xato - noto'g'ri kiritmada nima bo'ladi?
Yuqoridagi ortacha funksiyasi uchun uchinchi savol
javobsiz qolgan edi: bo'sh massivda u NaN qaytaradi. Bu
xato emas, lekin kutilganmi?
Testlar aynan shu savolni berishga majbur qiladi - va ko'pincha xato test yozayotganda topiladi, ishga tushirganda emas.
node --testbilan birinchi testni yozing.assert/strictva oddiyassertfarqini sinang.deepEqualbilan obyektlarni solishtiring.assert.throwsbilan xatoni tekshiring.describevaitbilan guruhlang.- Test nomlarini jumla shaklida yozing.
- Express ilovasini
beforevaafterbilan sinang. beforeEachga o'tkazib, testlarni mustaqil qiling.--experimental-test-coverageni ishga tushiring.node --inspect-brkbilan to'xtash nuqtasi qo'ying.
Xulosa #
- Node 18 dan beri test yurituvchi ichida - paket kerak emas.
- Har doim
node:assert/strict- bo'shassertyolg'on ishonch beradi. describe/itvatest- bir xil ish, boshqa uslub.- Test nomi nima kutilayotganini aytsin.
- Express ilovasini
listen(0)bilan ko'tarib,fetchbilan sinang. - Testlar mustaqil bo'lsin -
beforeEachbilan toza holat. - Qamrov 100% bo'lishi hech narsani isbotlamaydi; muhimi - qaysi holatlar sinalgani.
- Chuqur nosozlik uchun
console.logemas,node --inspect.
Keyingi bo'limda hammasini birlashtirib, to'liq API yozamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.