19-bo‘lim
Testlash
node:test bilan sinovlar, turlar ish vaqtida tekshirilmasligi va tur sinovlarini yozishning uch usuli.
Ushbu bo‘lim mundarijasi
Sinovlar va turlar bir-birini almashtirmaydi - ular turli narsalarni tekshiradi.
Bu bobda ikkalasini ham yozamiz va nima uchun ikkalasi ham kerakligini aniq ko'ramiz.
Node.js 18 dan beri sinov ishga tushirgichi o'zida bor - qo'shimcha paket kerak emas.
Birinchi sinov #
export function chegirma(narx: number, foiz: number): number {
return narx - (narx * foiz) / 100;
}
import { test } from "node:test";
import assert from "node:assert/strict";
import { chegirma } from "./dokon";
test("chegirma foizni ayiradi", () => {
assert.equal(chegirma(50000, 20), 40000);
});
test("nol foiz narxni ozgartirmaydi", () => {
assert.equal(chegirma(50000, 0), 50000);
});
node --import tsx --test src/dokon.test.ts 2>&1 \
| grep -E '^(ok|not ok|# (tests|pass|fail))'
ok 1 - chegirma foizni ayiradi
ok 2 - nol foiz narxni ozgartirmaydi
# tests 2
# pass 2
# fail 0
node --import tsx TypeScript ni to'g'ridan-to'g'ri ishga
tushiradi - oldindan kompilyatsiya qilish shart emas.
Chiqish TAP formatida bo'ladi va har bir sinov uchun
duration_ms qatorini ham chiqaradi. Bu qiymat har safar
o'zgaradi, shuning uchun darslikda faqat natija qatorlarini
ajratib ko'rsatamiz - grep aynan shuning uchun. O'zingizda
grep siz ishga tushiring: to'liq chiqish ancha batafsil.
node:assert/strict - assert.equal ni === ga aylantiradi.
Oddiy node:assert da u == ishlatadi va "5" == 5 o'tib
ketadi.
Eng ko'p ishlatiladigan tasdiqlashlar:
| Yozuv | Nimani tekshiradi |
|---|---|
assert.equal(a, b) | a === b (strict rejimda) |
assert.deepEqual(a, b) | Obyekt va massivlarni ichma-ich |
assert.ok(x) | x rost qiymat ekanini |
assert.throws(f) | f istisno tashlashini |
await assert.rejects(p) | p rad etilishini |
assert.match(s, /re/) | Satr naqshga mos kelishini |
Obyektlarni solishtirganda equal emas, deepEqual
kerak: ikkita alohida obyekt hech qachon === bo'lmaydi.
Xato bo'lsa:
export function chegirma(narx: number, foiz: number): number {
return narx - foiz;
}
import { test } from "node:test";
import assert from "node:assert/strict";
import { chegirma } from "./dokon";
test("chegirma foizni ayiradi", () => {
assert.equal(chegirma(50000, 20), 40000);
});
node --import tsx --test src/dokon.test.ts 2>&1 \
| grep -E '^(ok|not ok|# (tests|pass|fail))'
not ok 1 - chegirma foizni ayiradi
# tests 1
# pass 0
# fail 1
Funksiya 50000 - 20 ni hisobladi - turlar jihatidan hammasi
to'g'ri, mantiq esa noto'g'ri. Turlar buni hech qachon tuta
olmaydi.
describe bilan guruhlash #
export type Mahsulot = { nomi: string; narx: number };
export function jami(mahsulotlar: Mahsulot[]): number {
return mahsulotlar.reduce((a, m) => a + m.narx, 0);
}
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { jami, type Mahsulot } from "./dokon";
describe("jami", () => {
it("bosh royxat uchun nol", () => {
assert.equal(jami([]), 0);
});
it("narxlarni qoshadi", () => {
const m: Mahsulot[] = [
{ nomi: "Olma", narx: 12000 },
{ nomi: "Anor", narx: 25000 },
];
assert.equal(jami(m), 37000);
});
});
node --import tsx --test src/dokon.test.ts 2>&1 \
| grep -E '^(ok|not ok|# (tests|pass|fail))'
ok 1 - jami
# tests 2
# pass 2
# fail 0
import { jami, type Mahsulot } - 11-bobdagi tur importining
qisqa shakli: bitta qatorda ham qiymat, ham tur.
Sinov turni tekshira olmaydi #
Bu - bobning eng muhim qismi:
import { test } from "node:test";
import assert from "node:assert/strict";
type Mahsulot = { nomi: string; narx: number };
const javob = { nomi: "Olma", narx: "12000" };
const m = javob as unknown as Mahsulot;
test("narx son bolishi kerak", () => {
assert.equal(typeof m.narx, "number");
});
echo "--- tsc ---"
tsc --noEmit && echo "xato yo_q"
echo "--- sinov ---"
node --import tsx --test src/tur.test.ts 2>&1 \
| grep -E '^(ok|not ok|# (tests|pass|fail))'
--- tsc ---
xato yo_q
--- sinov ---
not ok 1 - narx son bolishi kerak
# tests 1
# pass 0
# fail 1
m ning turi Mahsulot deb aytilgan, m.narx esa number.
Lekin ish vaqtida u satr edi.
Sabab 1-bobdan ma'lum: turlar kompilyatsiyadan keyin
yo'qoladi. typeof m.narx haqiqiy qiymatga qaraydi, turga
emas.
Tur sinovlari: @ts-expect-error #
Turlarning o'zini tekshirish uchun ish vaqti kerak emas - kompilyatorning o'zi yetarli:
export function chegirma(narx: number, foiz: number): number {
return narx - (narx * foiz) / 100;
}
import { chegirma } from "./dokon";
chegirma(50000, 20);
// @ts-expect-error - satr qabul qilinmasligi kerak
chegirma("50000", 20);
// @ts-expect-error - ikkita argument talab qilinadi
chegirma(50000);
export {};
tsc --noEmit && echo "tur sinovi otdi"
tur sinovi otdi
Bu fayl hech qachon ishga tushmaydi. Uning vazifasi -
tsc ni o'tkazish.
Endi kimdir imzoni bo'shashtirsa:
export function chegirma(narx: any, foiz: any): any {
return narx - (narx * foiz) / 100;
}
import { chegirma } from "./dokon";
// @ts-expect-error - satr qabul qilinmasligi kerak
chegirma("50000", 20);
export {};
tsc --noEmit 2>&1 | fold -sw 74
src/dokon.tur-sinov.ts(3,1): error TS2578: Unused '@ts-expect-error'
directive.
18-bobda ko'rgan TS2578 bu yerda sinovning yiqilishi
sifatida ishlaydi: kutilgan xato yo'qolgan, demak himoya
zaiflashgan.
Tur sinovlari: turlar tengligi #
@ts-expect-error "bu xato bo'lishi kerak" deydi. Aniq turni
tekshirish uchun boshqa usul kerak:
export function chegirma(narx: number, foiz: number): number {
return narx - (narx * foiz) / 100;
}
import { chegirma } from "./dokon";
type Teng<A, B> =
(<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2
? true
: false;
const sinov1: Teng<ReturnType<typeof chegirma>, number> = true;
const sinov2: Teng<Parameters<typeof chegirma>, [number, number]> = true;
console.log(sinov1, sinov2);
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/teng.ts
tsc: xato yo_q
true true
export function chegirma(narx: number, foiz: number): string {
return String(narx - foiz);
}
import { chegirma } from "./dokon";
type Teng<A, B> =
(<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2
? true
: false;
const sinov1: Teng<ReturnType<typeof chegirma>, number> = true;
tsc --noEmit 2>&1 | fold -sw 74
src/teng.ts(8,7): error TS2322: Type 'true' is not assignable to type
'false'.
Teng<A, B> false ga aylandi, true esa unga mos kelmadi.
Teng<A, B> qanday ishlaydiBu - TypeScript jamoasining o'zi ishlatadigan hiyla.
Ikkita umumlashtirilmagan funksiya turi solishtiriladi:
<T>() => T extends A ? 1 : 2 va shu narsaning B bilan
yozilgani. Kompilyator ularni faqat A va B aynan bir
xil bo'lgandagina teng deb topadi.
Oddiy A extends B ? ... yetarli emas, chunki any hamma
narsaga, never esa hamma narsadan o'tadi.
Amalda bu turni o'zingiz yozmaysiz - expect-type yoki
tsd paketini olasiz. Lekin ichkarida aynan shu turadi.
Soxta obyektlar va satisfies #
export type Mahsulot = { id: number; nomi: string; narx: number };
export type Ombor = {
topish: (id: number) => Promise<Mahsulot | null>;
saqlash: (m: Mahsulot) => Promise<void>;
};
export async function nomini(ombor: Ombor, id: number): Promise<string> {
const m = await ombor.topish(id);
return m === null ? "topilmadi" : m.nomi;
}
import { test } from "node:test";
import assert from "node:assert/strict";
import { nomini, type Ombor } from "./ombor";
const soxta = {
topish: async (id: number) =>
id === 1 ? { id: 1, nomi: "Olma", narx: 12000 } : null,
saqlash: async () => {},
} satisfies Ombor;
test("mavjud mahsulot nomini qaytaradi", async () => {
assert.equal(await nomini(soxta, 1), "Olma");
});
test("mavjud bolmaganda topilmadi", async () => {
assert.equal(await nomini(soxta, 99), "topilmadi");
});
node --import tsx --test src/ombor.test.ts 2>&1 \
| grep -E '^(ok|not ok|# (tests|pass|fail))'
ok 1 - mavjud mahsulot nomini qaytaradi
ok 2 - mavjud bolmaganda topilmadi
# tests 2
# pass 2
# fail 0
nomini funksiyasi Ombor turini qabul qiladi, ya'ni
haqiqiy ma'lumotlar bazasi kerak emas. Bu - bog'liqlikni
tashqaridan berish naqshi va u turlar bilan juda mos tushadi.
satisfies (14-bob) bu yerda aniq bir maqsad bilan
ishlatilgan - shartnoma buzilsa, sinov kompilyatsiyada
yiqiladi:
export type Mahsulot = { id: number; nomi: string; narx: number };
export type Ombor = {
topish: (id: number) => Promise<Mahsulot | null>;
saqlash: (m: Mahsulot) => Promise<void>;
};
import { type Ombor } from "./ombor";
const soxta = {
topish: async () => null,
} satisfies Ombor;
export {};
tsc --noEmit 2>&1 | fold -sw 74
src/ombor.test.ts(5,3): error TS1360: Type '{ topish: () =>
Promise<null>; }' does not satisfy the expected type 'Ombor'.
Property 'saqlash' is missing in type '{ topish: () => Promise<null>;
}' but required in type 'Ombor'.
as any yoki jest.fn() bilan yozilgan soxta obyekt haqiqiy
interfeys o'zgarganda jim qoladi. Sinov o'tadi, ishlab
chiqarish yiqiladi.
satisfies bilan yozilgan soxta obyekt esa darhol
ogohlantiradi.
Qoida: har bir soxta obyektga satisfies Interfeys qo'shing.
Jadvalli sinovlar #
export type Tolov =
| { usul: "naqd"; miqdor: number }
| { usul: "karta"; raqam: string };
export function tavsif(t: Tolov): string {
switch (t.usul) {
case "naqd":
return `naqd ${t.miqdor}`;
case "karta":
return `karta ${t.raqam}`;
default: {
const qolgan: never = t;
return qolgan;
}
}
}
import { test } from "node:test";
import assert from "node:assert/strict";
import { tavsif, type Tolov } from "./tolov";
const holatlar: Array<[Tolov, string]> = [
[{ usul: "naqd", miqdor: 12000 }, "naqd 12000"],
[{ usul: "karta", raqam: "8600" }, "karta 8600"],
];
for (const [kirish, kutilgan] of holatlar) {
test(`${kirish.usul} uchun togri tavsif`, () => {
assert.equal(tavsif(kirish), kutilgan);
});
}
node --import tsx --test src/tolov.test.ts 2>&1 \
| grep -E '^(ok|not ok|# (tests|pass|fail))'
ok 1 - naqd uchun togri tavsif
ok 2 - karta uchun togri tavsif
# tests 2
# pass 2
# fail 0
Array<[Tolov, string]> - kortejlar massivi (4-bob). Har bir
holat turlangan, shuning uchun noto'g'ri holat yozib bo'lmaydi.
Unionga uchinchi a'zo qo'shilsa, tavsif dagi never
tekshiruvi (6-bob) darhol ogohlantiradi - lekin sinov
jadvaliga yangi qator qo'shishni u talab qilmaydi. Buni qo'lda
kuzatish kerak.
Xulosa #
- Node.js da sinov ishga tushirgichi o'zida bor:
node:testvanode:assert/strict. node --import tsx --testTypeScript sinovlarini to'g'ridan-to'g'ri ishga tushiradi.node:assert/strict===ishlatadi; oddiynode:assertesa==.- Turlar va sinovlar turli xatolarni tutadi: turlar har bir qatorda, sinovlar esa siz yozgan holatlarda.
- Ish vaqtidagi sinov turni tekshira olmaydi - turlar kompilyatsiyadan keyin yo'qoladi.
@ts-expect-errorbilan "bu xato bo'lishi kerak" degan tur sinovini yozish mumkin; imzo bo'shashsaTS2578chiqadi.- Aniq turni tekshirish uchun
Teng<A, B>naqshi yokiexpect-type/tsdpaketi ishlatiladi. - Bog'liqlikni tashqaridan berish soxta obyekt yozishni osonlashtiradi.
- Har bir soxta obyektga
satisfies Interfeysqo'shing - shartnoma o'zgarsa, sinov kompilyatsiyada yiqiladi. - Jadvalli sinovda holatlar massivini turlang - noto'g'ri holat yozib bo'lmaydi.
node:testbilan ikkita sinov yozing va ularni ishga tushiring.- Funksiyani ataylab buzib,
not okchiqishini ko'ring. describevaitbilan guruhlang.as unknown asbilan yolg'on tur berib, sinov uni tutishini tasdiqlang.@ts-expect-errorbilan tur sinov fayli yozing.- Funksiya parametrini
anyqilib,TS2578ni oling. Teng<A, B>turini yozib, qaytish turini tekshiring.- Qaytish turini o'zgartirib, tur sinovini yiqiting.
- Interfeysga
satisfiesbilan soxta obyekt yozing. - Interfeysga yangi metod qo'shib, soxta obyekt kompilyatsiyada yiqilishini ko'ring.
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.