يدعم عداء الاختبار خطافات دورة الحياة التالية. هذا مفيد لتحميل عناصر اختبار الاختبار، ومحاكاة البيانات، وتكوين بيئة الاختبار.
| الخطاف | الوصف |
|---|---|
beforeAll | يعمل مرة واحدة قبل جميع الاختبارات. |
beforeEach | يعمل قبل كل اختبار. |
afterEach | يعمل بعد كل اختبار. |
afterAll | يعمل مرة واحدة بعد جميع الاختبارات. |
onTestFinished | يعمل بعد انتهاء اختبار واحد (بعد جميع afterEach). |
الإعداد والتفكيك لكل اختبار
قم بتنفيذ منطق الإعداد والتفكيك لكل اختبار مع beforeEach و afterEach.
import { beforeEach, afterEach, test } from "bun:test";
beforeEach(() => {
console.log("تشغيل الاختبار.");
});
afterEach(() => {
console.log("تم الانتهاء من الاختبار.");
});
// الاختبارات...
test("مثال اختبار", () => {
// سيتم تشغيل beforeEach قبل هذا الاختبار
// و afterEach بعده
});الإعداد والتفكيك لكل نطاق
قم بتنفيذ منطق الإعداد والتفكيك لكل نطاق مع beforeAll و afterAll. يتم تحديد النطاق بواسطة مكان تعريف الخطاف.
محدد بنطاق كتلة Describe
لتحديد نطاق الخطافات لكتلة describe معينة:
import { describe, beforeAll, afterAll, test } from "bun:test";
describe("مجموعة الاختبار", () => {
beforeAll(() => {
// الإعداد لمجموعة describe هذه
console.log("إعداد مجموعة الاختبار");
});
afterAll(() => {
// التفكيك لمجموعة describe هذه
console.log("تفكيك مجموعة الاختبار");
});
test("الاختبار 1", () => {
// تنفيذ الاختبار
});
test("الاختبار 2", () => {
// تنفيذ الاختبار
});
});محدد بنطاق ملف الاختبار
لتحديد نطاق الخطافات لملف الاختبار بأكمله:
import { describe, beforeAll, afterAll, test } from "bun:test";
beforeAll(() => {
// الإعداد للملف بأكمله
console.log("إعداد ملف الاختبار");
});
afterAll(() => {
// التفكيك للملف بأكمله
console.log("تفكيك ملف الاختبار");
});
describe("مجموعة الاختبار", () => {
test("الاختبار 1", () => {
// تنفيذ الاختبار
});
});onTestFinished
استخدم onTestFinished لتشغيل دالة استدعاء بعد انتهاء اختبار واحد. يعمل بعد جميع خطافات afterEach.
import { test, onTestFinished } from "bun:test";
test("التنظيف بعد الاختبار", () => {
onTestFinished(() => {
// يعمل بعد جميع خطافات afterEach
console.log("انتهى الاختبار");
});
});غير مدعوم في الاختبارات المتزامنة؛ استخدم test.serial بدلاً من ذلك.
الإعداد والتفكيك العام
لتحديد نطاق الخطافات لتشغيل اختبار متعدد الملفات بأكمله، عرّف الخطافات في ملف منفصل.
import { beforeAll, afterAll } from "bun:test";
beforeAll(() => {
// الإعداد العام
console.log("إعداد الاختبار العام");
// تهيئة اتصالات قاعدة البيانات، بدء الخوادم، إلخ.
});
afterAll(() => {
// التفكيك العام
console.log("تفكيك الاختبار العام");
// إغلاق اتصالات قاعدة البيانات، إيقاف الخوادم، إلخ.
});ثم استخدم --preload لتشغيل نص الإعداد قبل أي ملفات اختبار.
bun test --preload ./setup.tsلتجنب كتابة --preload في كل مرة تشغل فيها الاختبارات، يمكن إضافتها إلى bunfig.toml:
[test]
preload = ["./setup.ts"]أمثلة عملية
إعداد قاعدة البيانات
import { beforeAll, afterAll, beforeEach, afterEach } from "bun:test";
import { createConnection, closeConnection, clearDatabase } from "./db";
let connection;
beforeAll(async () => {
// الاتصال بقاعدة بيانات الاختبار
connection = await createConnection({
host: "localhost",
database: "test_db",
});
});
afterAll(async () => {
// إغلاق اتصال قاعدة البيانات
await closeConnection(connection);
});
beforeEach(async () => {
// البدء بقاعدة بيانات نظيفة لكل اختبار
await clearDatabase(connection);
});إعداد خادم API
import { beforeAll, afterAll } from "bun:test";
import { startServer, stopServer } from "./server";
let server;
beforeAll(async () => {
// بدء خادم الاختبار
server = await startServer({
port: 3001,
env: "test",
});
});
afterAll(async () => {
// إيقاف خادم الاختبار
await stopServer(server);
});إعداد المحاكاة
import { beforeEach, afterEach } from "bun:test";
import { mock } from "bun:test";
beforeEach(() => {
// إعداد المحاكاة الشائعة
mock.module("./api-client", () => ({
fetchUser: mock(() => Promise.resolve({ id: 1, name: "مستخدم اختبار" })),
createUser: mock(() => Promise.resolve({ id: 2 })),
}));
});
afterEach(() => {
// مسح جميع المحاكاة بعد كل اختبار
mock.restore();
});خطافات دورة الحياة غير المتزامنة
تدعم جميع خطافات دورة الحياة الدوال غير المتزامنة:
import { beforeAll, afterAll, test } from "bun:test";
beforeAll(async () => {
// إعداد غير متزامن
await new Promise(resolve => setTimeout(resolve, 100));
console.log("اكتمل الإعداد غير المتزامن");
});
afterAll(async () => {
// تفكيك غير متزامن
await new Promise(resolve => setTimeout(resolve, 100));
console.log("اكتمل التفكيك غير المتزامن");
});
test("اختبار غير متزامن", async () => {
// سينتظر الاختبار اكتمال beforeAll
await expect(Promise.resolve("test")).resolves.toBe("test");
});خطافات متداخلة
يمكن تداخل الخطافات وستعمل بالترتيب المناسب:
import { describe, beforeAll, beforeEach, afterEach, afterAll, test } from "bun:test";
beforeAll(() => console.log("File beforeAll"));
afterAll(() => console.log("File afterAll"));
describe("outer describe", () => {
beforeAll(() => console.log("Outer beforeAll"));
beforeEach(() => console.log("Outer beforeEach"));
afterEach(() => console.log("Outer afterEach"));
afterAll(() => console.log("Outer afterAll"));
describe("inner describe", () => {
beforeAll(() => console.log("Inner beforeAll"));
beforeEach(() => console.log("Inner beforeEach"));
afterEach(() => console.log("Inner afterEach"));
afterAll(() => console.log("Inner afterAll"));
test("اختبار متداخل", () => {
console.log("تشغيل الاختبار");
});
});
});// ترتيب الإخراج:
// File beforeAll
// Outer beforeAll
// Inner beforeAll
// Outer beforeEach
// Inner beforeEach
// Test running
// Inner afterEach
// Outer afterEach
// Inner afterAll
// Outer afterAll
// File afterAllمعالجة الأخطاء
إذا طرح خطاف دورة حياة خطأ، فسيؤثر على تنفيذ الاختبار:
import { beforeAll, test } from "bun:test";
beforeAll(() => {
// إذا طرح هذا، سيتم تخطي جميع الاختبارات في هذا النطاق
throw new Error("فشل الإعداد");
});
test("سيتم تخطي هذا الاختبار", () => {
// لن يعمل هذا لأن beforeAll فشل
});لمعالجة أخطاء أفضل:
import { beforeAll, test, expect } from "bun:test";
beforeAll(async () => {
try {
await setupDatabase();
} catch (error) {
console.error("فشل إعداد قاعدة البيانات:", error);
throw error; // إعادة الرمي لفشل مجموعة الاختبار
}
});أفضل الممارسات
اجعل الخطافات بسيطة
// جيد: إعداد بسيط ومركّز
beforeEach(() => {
clearLocalStorage();
resetMocks();
});
// تجنب: منطق معقد في الخطافات
beforeEach(async () => {
// الكثير من المنطق المعقد يجعل الاختبارات صعبة التصحيح
const data = await fetchComplexData();
await processData(data);
await setupMultipleServices(data);
});استخدم النطاق المناسب
// جيد: إعداد على مستوى الملف للموارد المشتركة
beforeAll(async () => {
await startTestServer();
});
// جيد: إعداد على مستوى الاختبار للحالة الخاصة بالاختبار
beforeEach(() => {
user = createTestUser();
});تنظيف الموارد
import { afterAll, afterEach } from "bun:test";
afterEach(() => {
// التنظيف بعد كل اختبار
document.body.innerHTML = "";
localStorage.clear();
});
afterAll(async () => {
// تنظيف الموارد المكلفة
await closeDatabase();
await stopServer();
});