Mojtaba Pourkhanlar
About meProjectsBlog

  • 👤About me
  • 🧰Projects
  • ✍️Blog

React Testing Guide


مقدمه


تست‌نویسی یکی از مهم‌ترین بخش‌های توسعه نرم‌افزارهای مدرن است.
در React، تست‌ها به ما کمک می‌کنند:

  • از درست کار کردن کامپوننت‌ها مطمئن شویم
  • جلوی Regression Bug‌ها را بگیریم
  • با خیال راحت Refactor کنیم
  • رفتار واقعی کاربر را شبیه‌سازی کنیم

در این مقاله، سه نوع اصلی تست را بررسی می‌کنیم:

  1. Unit Test
  2. Integration Test
  3. End-to-End (E2E) Test

ابزارهای مورد استفاده

  • Jest → Test Runner و Assertion Library
  • React Testing Library (RTL) → شبیه‌سازی رفتار کاربر در UI
  • Cypress / Playwright → تست End-to-End در مرورگر واقعی

1️⃣ Unit Test


خب Unit Test کوچک‌ترین واحد قابل تست در برنامه را بررسی می‌کند.

در React معمولاً شامل:

  • یک تابع
  • یا یک کامپوننت مستقل

هدف Unit Test

  • بررسی منطق داخلی
  • اجرای سریع
  • عدم وابستگی به API، Router، Database و ...

چه چیزهایی را Unit Test می‌کنیم؟

  • State changes
  • Props
  • Event handlers
  • Conditional rendering

مثال: Unit Test یک Counter Component

// Counter.js
import React from "react";
export function Counter() {
  const [count, setCount]
 = React.useState(0);

  return (
    <div>
      <p data-testid="count">{count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

// Counter.test.js
import { render, screen, fireEvent } from "@testing-library/react";
import { Counter } from "./Counter";

test("افزایش شمارنده بعد از کلیک", () => {
  render(<Counter />);
  fireEvent.click(screen.getByText("+1"));
  expect(screen.getByTestId("count").textContent).toBe("1");
});

مزایا

  • سریع
  • ساده
  • مناسب CI/CD

معایب

  • رفتار واقعی کل سیستم را نشان نمی‌دهد
  • تعامل بین کامپوننت‌ها را پوشش نمی‌دهد

2️⃣ Integration Test


خب Integration Test بررسی می‌کند که چند بخش از سیستم (کامپوننت‌ها، هوک‌ها، سرویس‌ها)
در کنار هم درست با هم کار می‌کنند یا نه.

برخلاف Unit Test:

  • تست فقط روی یک کامپوننت نیست
  • تعامل بین کامپوننت‌ها بررسی می‌شود
  • رفتار کاربر شبیه‌سازی می‌شود

هدف Integration Test

  • اطمینان از ارتباط صحیح بین اجزا
  • بررسی Flowهای اصلی برنامه
  • کاهش باگ‌های ناشی از اتصال اشتباه کامپوننت‌ها

چه چیزهایی در Integration Test بررسی می‌شود؟

  • تعامل parent و child component
  • تغییر state بر اساس رویدادها
  • ارسال props
  • تعامل فرم‌ها با handlerها
  • ارتباط UI با logic

ابزارهای رایج

  • Jest
  • React Testing Library (RTL)

-خب RTL در Integration Test بسیار مهم است چون تمرکز آن روی رفتار کاربر است.


مثال: Integration Test فرم لاگین

// LoginForm.js
import React from "react";

export function LoginForm({ onLogin }) {
  const [username, setUsername]
 = React.useState("");

  return (
    <form
      onSubmit={e => {
        e.preventDefault();
        onLogin(username);
      }}
    >
      <input
        placeholder="username"
        value={username}
        onChange={e => setUsername(e.target.value)}
      />
      <button type="submit">Login</button>
    </form>
  );
}
// LoginForm.test.js
import { render, screen, fireEvent } from "@testing-library/react";
import { LoginForm } from "./LoginForm";

test("ارسال نام کاربری بعد از submit", () => {
  const handleSubmit = jest.fn();

  render(<LoginForm onSubmit={handleSubmit} />);

  fireEvent.change(screen.getByPlaceholderText("username"), {
    target: { value: "mojtaba" },
  });

  fireEvent.click(screen.getByText("Login"));

  expect(handleSubmit).toHaveBeenCalledWith("mojtaba");
});

مزایا

  • تست تعامل واقعی‌تر
  • پوشش سناریوهای مهم

معایب

  • کندتر از Unit Test
  • پیچیدگی بیشتر

3️⃣ End-to-End (E2E) Test


خب End-to-End Test کل برنامه را مثل یک کاربر واقعی تست می‌کند.

از UI تا API و حتی Routing و Network را پوشش می‌دهد.


هدف E2E

  • اطمینان از عملکرد کل سیستم در محیط واقعی
  • شبیه‌سازی تعاملات کاربر در مرورگر

ابزارهای رایج

  • Cypress
  • PlayWright

مثال E2E با Cypress

describe("Counter App", () => {
  it("باید شمارنده را افزایش دهد", () => {
    cy.visit("http://localhost:3000");
    cy.contains("+1").click();
    cy.get("[data-testid='count']
").should("have.text", "1");
  });
});

مثال E2E با Playwright

import { test, expect } from "@playwright/test";

test("افزایش شمارنده", async ({ page }) => {
  await page.goto("http://localhost:3000");
  await page.click("text=+1");
  await expect(page.locator("[data-testid='count']
")).toHaveText("1");
});

مزایا

  • کشف باگ‌های واقعی
  • نزدیک‌ترین تست به واقعیت

معایب

  • زمان اجرای طولانی‌تر
  • نیازمند محیط واقعی برای اجرا (Server/Browser)
  • هزینه نگهداری بالا


مقایسه نهایی انواع تست

نوع تست سرعت هزینه پوشش
Unit Test ⚡⚡⚡ کم محدود
Integration Test ⚡⚡ متوسط خوب
E2E Test ⚡ زیاد کامل

جمع‌بندی

بهترین استراتژی تست در React:

  • خب Unit Test زیاد بنویسید → برای logic و کامپوننت‌های کوچک
  • و Integration Test کافی بنویسید → برای تعامل بین بخش‌ها
  • و E2E Test هدفمند بنویسید → برای سناریوهای حیاتی مثل Login، Checkout و Payment

تست خوب یعنی اعتماد به سیستم، نه فقط افزایش Coverage.