خبر و ترفند روز

خبر و ترفند های روز را اینجا بخوانید!

چگونه API های Express را با Jest آزمایش کنیم

استفاده از Express برای ایجاد API و Jest برای آزمایش آن ترکیبی برنده برای بهبود قابلیت اطمینان برنامه شما است.

آزمایش، در حالی که می تواند زمان بر باشد، گام مهمی در چرخه توسعه هر برنامه ای است. این تضمین می‌کند که قبل از اینکه کد را به سمت تولید فشار دهید، باگ‌ها و مشکلات را زودتر متوجه می‌شوید.

می‌توانید از Jest برای آزمایش API Express Rest استفاده کنید. پس از ایجاد یک CRUD API ساده، نحوه نوشتن تست برای هر نقطه پایانی را بیابید.

جست چیست؟

کتابخانه‌های آزمایشی جاوا اسکریپت زیادی وجود دارد که می‌توانید از بین آنها انتخاب کنید، اما Jest ساده‌ترین راه برای شروع است. این یک کتابخانه آزمایشی است که توسط فیس بوک توسعه یافته و بیشتر برای آزمایش پروژه های React استفاده می شود. با این حال، می توانید از آن برای آزمایش Node و سایر پروژه های مبتنی بر جاوا اسکریپت نیز استفاده کنید. این ابزار در بالای Jasmine، یکی دیگر از ابزارهای آزمایش، توسعه یافته است، و همراه با کتابخانه ادعای خود همراه است.

در حالی که برای نوشتن تست‌ها در Jest نیازی به کتابخانه ادعا ندارید، باید از ابزاری برای درخواست HTTP استفاده کنید. این مقاله از SuperTest استفاده می کند.

سوپرتست چیست؟

SuperTest یک کتابخانه تست گره برای تماس های HTTP است. کتابخانه آزمایش ابرعامل را گسترش می دهد و به شما امکان می دهد درخواست هایی مانند GET، POST، PUT و DELETE را ارسال کنید.

SuperTest یک شی درخواست ارائه می دهد که می توانید از آن برای ایجاد درخواست های HTTP استفاده کنید.

const request = require("supertest")
request("https://icanhazdadjoke.com")
.get('/slack')
.end(function(err, res) {
if (err) throw err;
console.log(res.body.attachments);
});

در اینجا، URL پایه API را به شی درخواست ارسال می کنید و سپس متد HTTP را با بقیه URL زنجیره می زنید. متد end() سرور API را فراخوانی می کند و تابع callback پاسخ آن را مدیریت می کند.

هنگامی که پاسخ را از API دریافت کردید، می توانید از Jest برای اعتبارسنجی آن استفاده کنید.

یک Express API ایجاد کنید

برای آزمایش نقاط پایانی API خود، ابتدا باید یک REST API ایجاد کنید. API که ایجاد خواهید کرد بسیار ساده است. موارد را از یک آرایه درج، بازیابی، به روز رسانی و حذف می کند.

با ایجاد یک دایرکتوری جدید به نام node-jest و مقداردهی اولیه npm شروع کنید.

mkdir node-jest
npm init -y

سپس یک فایل جدید به نام index.js ایجاد کنید و سرور Express را ایجاد کنید.

const express = require("express")
const app = express()
app.listen(3000, () => console.log("Listening at port 3000"))

نقطه پایانی GET /todos را آزمایش کنید

اولین نقطه پایانی که ایجاد می کنید، نقطه پایانی GET /todos است. تمام موارد موجود در آرایه را برمی گرداند. در index.js موارد زیر را اضافه کنید.

const todos = [
];
// Get all todos
app.get("/todos", (req, res) => {
  returnres.status(200).json({
    data: todos,
    error: null,
  });
});

توجه داشته باشید که پاسخ دارای یک کد وضعیت 200 و یک شی JSON است که شامل مورد to-dos در آرایه ای به نام داده و یک پیام خطا است. این همان چیزی است که با استفاده از Jest آزمایش خواهید کرد.

مطلب مرتبط:   نحوه استفاده از React Context برای مدیریت حالت در Next.js 13

اکنون، Jest و SuperTest را نصب کنید:

npm install jest supertest

سپس یک اسکریپت آزمایشی را به صورت زیر در package.json اضافه کنید:

{
  "scripts": {
    "test": "jest"
  }
}

قبل از شروع نوشتن تست های خود، باید نحوه نوشتن یک تست پایه را در Jest بدانید.

تابع زیر را در نظر بگیرید:

functionsum(a, b) {
    return a + b;
}

module.exports = sum;

در فایل تست، شما باید:

  • تابع را وارد کنید.
  • آنچه را که آزمایش باید انجام دهد را توضیح دهید.
  • تابع را فراخوانی کنید.
  • پاسخ مورد انتظار را با پاسخ واقعی تابع ثابت کنید.

const { sum } = require("./sum")

describe("Sum of two items", async() => {
    test("It should return 4", () => {
        expect(sum(2,2)).toBe(4)
    })
})

کلمه کلیدی describe گروه تست ها را مشخص می کند و عبارت test تست خاص را مشخص می کند. اگر مقدار بازگردانده شده از تابع با مقدار ارسال شده به toBe مطابقت داشته باشد، تست انجام می شود.

هنگام آزمایش نقاط پایانی API، یک تابع را فراخوانی نمی‌کنید، بلکه درخواستی را با استفاده از SuperTest یا کتابخانه مشتری HTTP دیگری ارسال می‌کنید.

با بازگشت به نقطه پایانی GET، یک فایل جدید به نام api.test.js ایجاد کنید. اینجاست که تمام تست های نقطه پایانی را می نویسید. نامگذاری فایل آزمایشی با .test infix تضمین می کند که Jest آن را به عنوان یک فایل آزمایشی تشخیص می دهد.

در api.test.js، supertest را وارد کنید و URL پایه را به این صورت تنظیم کنید:

const request = require("supertest")
const baseURL = "http://localhost:3000"

سپس، اولین تست را در بلوک توصیف ایجاد کنید:

describe("GET /todos", () => {
  const newTodo = {
    id: crypto.randomUUID(),
    item: "Drink water",
    completed: false,
  }
  beforeAll(async () => {
    // set up the todo
    await request(baseURL).post("/todo").send(newTodo);
  })
  afterAll(async () => {
    await request(baseURL).delete(`/todo/${newTodo.id}`)
  })
  it("should return 200", async () => {
    const response = await request(baseURL).get("/todos");
    expect(response.statusCode).toBe(200);
    expect(response.body.error).toBe(null);
  });
  it("should return todos", async () => {
    const response = await request(baseURL).get("/todos");
    expect(response.body.data.length >= 1).toBe(true);
  });
});

قبل از اجرای تست‌ها، باید عملکردهای راه‌اندازی و حذف را تعریف کنید. این توابع آرایه todo را با یک آیتم قبل از آزمایش پر می کنند و داده های ساختگی را بعد از هر آزمایش حذف می کنند.

مطلب مرتبط:   نحوه تغییر مسیر کاربر پس از ورود به React

کدی که قبل از تمام تست ها اجرا می شود در تابع () BeforeAll قرار دارد. کدی که بعد از تمام تست ها اجرا می شود در تابع afterAll() است.

در این مثال، شما به سادگی نقطه پایانی POST و DELETE را برای هر کدام می زنید. در یک برنامه واقعی، احتمالاً به یک پایگاه داده ساختگی که حاوی داده های آزمایشی است متصل می شوید.

در این تست، ابتدا درخواستی به نقطه پایانی GET /todos ارسال کردید و پاسخ ارسال شده را با نتایج مورد انتظار مقایسه کردید. اگر پاسخ کد وضعیت HTTP 200 داشته باشد، داده ها خالی نباشد و پیام خطا تهی باشد، این مجموعه آزمایشی قبول می شود.

نقطه پایانی POST /todo را آزمایش کنید

در index.js، نقطه پایانی POST /todo را ایجاد کنید:

app.post("/todo", (req, res) => {
  try {
    const { id, item, completed } = req.body;
    const newTodo = {
      id,
      item,
      completed,
    };
    todos.push(newTodo);
    returnres.status(201).json({
      data: todos,
      error: null,
    });
  } catch (error) {
    returnres.status(500).json({
      data: null,
      error: error,
    });
  }
});

در این تست، باید جزئیات todo را با استفاده از متد send() در بدنه درخواست ارسال کنید.

request(baseURL).post("/todo").send(newTodo)

درخواست POST /todo باید یک کد وضعیت 201 و آرایه todos را با آیتم جدید اضافه شده در پایان بازگرداند. در اینجا آزمایش ممکن است شبیه باشد:

describe("POST /todo", () => {
  const newTodo = {
    // todo
  }
  afterAll(async () => {
    await request(baseURL).delete(`/todo/${newTodo.id}`)
  })
  it("should add an item to todos array", async () => {
    const response = await request(baseURL).post("/todo").send(newTodo);
    const lastItem = response.body.data[response.body.data.length-1]
    expect(response.statusCode).toBe(201);
    expect(lastItem.item).toBe(newTodo["item"]);
    expect(lastItem.completed).toBe(newTodo["completed"]);
  });
});

در اینجا، شما داده های todo را به عنوان آرگومان به متد send() منتقل می کنید. پاسخ باید دارای کد وضعیت 201 باشد و همچنین شامل تمام موارد انجام کار در یک شی داده باشد. برای آزمایش اینکه آیا todo واقعا ایجاد شده است، بررسی کنید که آیا آخرین ورودی در کارهای بازگشتی با آنچه در درخواست ارسال کرده اید مطابقت دارد یا خیر.

نقطه پایانی PUT /todos/:id باید مورد به روز شده را برگرداند:

app.put("/todos/:id", (req, res) => {
  try {
    const id = req.params.id
    const todo = todos.find((todo) => todo.id == id);
    if(!todo) {
      thrownewError("Todo not found")
    }
    todo.completed = req.body.completed;
    returnres.status(201).json({
      data: todo,
      error: null,
    });
  } catch (error) {
    returnres.status(500).json({
      data: null,
      error: error,
    });
  }
});

پاسخ را به صورت زیر تست کنید:

describe("Update one todo", () => {
  const newTodo = {
    // todo
  }
  beforeAll(async () => {
    await request(baseURL).post("/todo").send(newTodo);
  })
  afterAll(async () => {
    await request(baseURL).delete(`/todo/${newTodo.id}`)
  })
  it("should update item if it exists", async () => {
    const response = await request(baseURL).put(`/todos/${newTodo.id}`).send({
      completed: true,
    });
    expect(response.statusCode).toBe(201);
    expect(response.body.data.completed).toBe(true);
  });
});

مقدار تکمیل شده در بدنه پاسخ باید درست باشد. به یاد داشته باشید که شناسه موردی را که می‌خواهید به‌روزرسانی کنید در URL قرار دهید.

مطلب مرتبط:   شروع با Vite: The Ultimate Build Tool

نقطه پایانی DELETE /todos/:id را آزمایش کنید

در index.js، نقطه پایانی DELETE را ایجاد کنید. باید داده های todo را بدون آیتم حذف شده برگرداند.

app.delete("/todos/:id", (req, res) => {
  try {
    const id = req.params.id
    const todo = todos[0]
    if(todo) {
      todos.splice(id, 1)
    }
    returnres.status(200).json({
      data: todos,
      error: null,
    });
  } catch (error) {
    returnres.status(500).json({
      data: null,
      error: error,
    });
  }
});

برای آزمایش نقطه پایانی، می توانید بررسی کنید که آیا مورد حذف شده همچنان در داده های برگشتی وجود دارد یا خیر:

describe("Delete one todo", () => {
  const newTodo = {
    // todo
  }
  beforeAll(async () => {
    await request(baseURL).post("/todo").send(newTodo);
  })
  it("should delete one item", async () => {
    const response = await request(baseURL).delete(`/todos/${newTodo.id}`);
    const todos = response.body.data
    const exists = todos.find(todo => {
      newTodo.id == todoId
    })
    expect(exists).toBe(undefined)
  });
});

داده های برگشتی از نقطه پایانی DELETE نباید حاوی آیتم حذف شده باشد. از آنجایی که موارد برگردانده شده در یک آرایه هستند، می توانید از Array[id] برای بررسی اینکه آیا API مورد را به درستی حذف کرده است یا خیر استفاده کنید. نتیجه باید نادرست باشد.

ایجاد REST API

در این مقاله، نحوه آزمایش API Express Rest با استفاده از Jest API را یاد گرفتید. شما آزمایش هایی را برای درخواست های HTTP GET، PUT، POST و DELETE نوشتید و دیدید که چگونه داده ها را به نقطه پایانی در URL و درخواست ارسال کنید. شما باید بتوانید این دانش را هنگام آزمایش Rest API خود به کار ببرید.