19-bo‘lim

Testlar va nosozliklar

node:test bilan test yozish, assert, describe va it, Express ilovasini sinash, qamrov va nosozlikni izlash usullari.

🕑 13 daqiqa o‘qish 📄 765 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Birinchi test
  2. assert moduli
  3. Testlarni haqiqatan ishga tushirish
  4. describe va it
  5. Express ilovasini sinash
  6. Testlar mustaqil bo'lsin
  7. Qamrov
  8. Nosozlikni izlash
  9. Qanday test yozish kerak
  10. Xulosa

Node 18 dan beri test yurituvchi tilning ichida bor. jest yoki mocha o'rnatish shart emas.

Birinchi test #

Testlar odatda *.test.mjs fayllarida yoziladi:

JavaScript
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:

Terminal
node --test

Natija taxminan shunday bo'ladi:

Natija
✔ qo'shish to'g'ri ishlaydi (1.2ms)

ℹ tests 1
ℹ pass 1
ℹ fail 0

assert moduli #

JavaScript
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);
}
Natija
Hamma tasdiq o'tdi
Xato nomi: AssertionError
Kutilgan: 2 haqiqiy: 1
TasdiqNimani tekshiradi
assert.equalQiymatlar teng (===)
assert.deepEqualObyekt/massiv ichma-ich teng
assert.okQiymat rost
assert.matchSatr naqshga mos
assert.throwsFunksiya xato tashlaydi
assert.rejectsVada rad etiladi
Doim node:assert/strict ishlating

node:assert ikki shaklda keladi:

JavaScript
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 #

JavaScript
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");
Natija
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 #

JavaScript
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:

Natija
▶ 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.

Test nomi jumla bo'lsin

Yaxshi test nomi nima kutilayotganini aytadi:

JavaScript
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 #

JavaScript
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;
}
JavaScript
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");
Natija
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.

IlgakQachon ishlaydi
beforeHamma testdan oldin bir marta
afterHamma testdan keyin bir marta
beforeEachHar bir testdan oldin
afterEachHar bir testdan keyin

Testlar mustaqil bo'lsin #

Testlar bir-biriga bog'liq bo'lmasin

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:

JavaScript
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 #

Terminal
node --test --experimental-test-coverage

Qamrov kodning necha foizi testlar bilan qamralganini ko'rsatadi.

Yuz foiz qamrov - maqsad emas

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:

JavaScript
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 #

JavaScript
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 },
]);
Natija
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.

VositaQachon
console.logTez tekshiruv
console.dir(x, { depth: null })Chuqur obyekt (15-bo'lim)
console.tableBir xil shakldagi qatorlar
node --inspectHaqiqiy nosozlik izlagich
node --inspect bilan to'xtash nuqtalari

console.log bilan izlash tez, lekin chegarasi bor: har safar kodni o'zgartirib, qayta ishga tushirish kerak.

Haqiqiy nosozlik izlagich:

Terminal
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 #

Uchta savol

Har bir funksiya uchun:

  1. Oddiy holat - hammasi to'g'ri bo'lganda nima qaytaradi?
  2. Chegara - bo'sh massiv, nol, eng katta qiymat, null?
  3. 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.

Amaliy topshiriq
  1. node --test bilan birinchi testni yozing.
  2. assert/strict va oddiy assert farqini sinang.
  3. deepEqual bilan obyektlarni solishtiring.
  4. assert.throws bilan xatoni tekshiring.
  5. describe va it bilan guruhlang.
  6. Test nomlarini jumla shaklida yozing.
  7. Express ilovasini before va after bilan sinang.
  8. beforeEach ga o'tkazib, testlarni mustaqil qiling.
  9. --experimental-test-coverage ni ishga tushiring.
  10. node --inspect-brk bilan to'xtash nuqtasi qo'ying.

Xulosa #

  • Node 18 dan beri test yurituvchi ichida - paket kerak emas.
  • Har doim node:assert/strict - bo'sh assert yolg'on ishonch beradi.
  • describe/it va test - bir xil ish, boshqa uslub.
  • Test nomi nima kutilayotganini aytsin.
  • Express ilovasini listen(0) bilan ko'tarib, fetch bilan sinang.
  • Testlar mustaqil bo'lsin - beforeEach bilan toza holat.
  • Qamrov 100% bo'lishi hech narsani isbotlamaydi; muhimi - qaysi holatlar sinalgani.
  • Chuqur nosozlik uchun console.log emas, node --inspect.

Keyingi bo'limda hammasini birlashtirib, to'liq API yozamiz.

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.