← Назад к вопросам

Как разработчик решает где писать тест?

2.0 Middle🔥 161 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Где писать тесты: Стратегия тестирования

Как опытный фронтенд-разработчик, я использую пирамиду тестов и определяю тип теста на основе того, что нужно проверить.

Пирамида тестов (Test Pyramid)

        /\
       /  \         E2E тесты (5-10%)
      /    \        Сложные, дорогие, медленные
     /------\
    /        \      Integration тесты (30-40%)
   /          \     Среднее, умеренно быстрые
  /------------|  
 /            |     Unit тесты (50-60%)
/             |     Быстрые, дешёвые, изолированные
/_____________|

Unit тесты (компонент в изоляции)

Когда писать unit тесты:

  • Логика компонента независима
  • Нужно проверить отдельную функцию
  • Тестируется поведение, а не взаимодействие
// файл: Button.test.tsx
import { render, screen } from "@testing-library/react";
import { Button } from "./Button";

describe("Button", () => {
  it("renders with correct text", () => {
    render(<Button>Click me</Button>);
    expect(screen.getByRole("button")).toHaveTextContent("Click me");
  });
  
  it("calls onClick handler when clicked", () => {
    const onClick = vitest.fn();
    render(<Button onClick={onClick}>Click</Button>);
    screen.getByRole("button").click();
    expect(onClick).toHaveBeenCalled();
  });
  
  it("applies correct CSS classes based on variant prop", () => {
    render(<Button variant="primary">Primary</Button>);
    expect(screen.getByRole("button")).toHaveClass("bg-blue-500");
  });
});

Integration тесты (несколько компонентов вместе)

Когда писать integration тесты:

  • Компоненты взаимодействуют друг с другом
  • Нужно проверить поток данных между компонентами
  • Используются реальные API запросы (мокированные)
// файл: LoginForm.test.tsx
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { LoginForm } from "./LoginForm";
import { server } from "@/tests/mocks/server";
import { http, HttpResponse } from "msw";

describe("LoginForm Integration", () => {
  it("submits form and shows success message", async () => {
    // Mock API response
    server.use(
      http.post("/api/login", () => {
        return HttpResponse.json({ token: "abc123" });
      })
    );
    
    render(<LoginForm />);
    
    await userEvent.type(screen.getByLabelText(/email/i), "test@example.com");
    await userEvent.type(screen.getByLabelText(/password/i), "password123");
    await userEvent.click(screen.getByRole("button", { name: /login/i }));
    
    await waitFor(() => {
      expect(screen.getByText(/success/i)).toBeInTheDocument();
    });
  });
});

E2E тесты (полный сценарий пользователя)

Когда писать E2E тесты:

  • Проверяется полный пользовательский сценарий
  • Нужны реальные браузер и сервер
  • Тестируется вся цепочка: UI -> API -> БД
// файл: login.e2e.spec.ts (Playwright)
import { test, expect } from "@playwright/test";

test.describe("Login flow", () => {
  test("user can login and see dashboard", async ({ page }) => {
    // Открыть страницу логина
    await page.goto("http://localhost:3000/login");
    
    // Заполнить форму
    await page.fill("[name=\"email\"]", "test@example.com");
    await page.fill("[name=\"password\"]", "password123");
    
    // Отправить форму
    await page.click("button[type=\"submit\"]");
    
    // Ждать перенаправления
    await page.waitForURL("http://localhost:3000/dashboard");
    
    // Проверить, что мы на dashboard
    expect(page.url()).toContain("/dashboard");
    expect(await page.isVisible("h1:has-text(\"Dashboard\")")).toBeTruthy();
  });
});

Принцип выбора: практический подход

Для простого компонента (Button, Badge, Icon):

// Писать: Unit тест
// Почему: компонент чистый, без логики, быстро тестировать

import { render, screen } from "@testing-library/react";
import { Badge } from "./Badge";

test("Badge displays correct color", () => {
  render(<Badge color="red">Error</Badge>);
  expect(screen.getByText("Error")).toHaveClass("bg-red-500");
});

Для компонента с логикой (Form, List с фильтрацией):

// Писать: Integration тест
// Почему: взаимодействие с дочерними компонентами, логика обновления состояния

describe("UserList with filtering", () => {
  it("filters users by name", async () => {
    const mockUsers = [
      { id: 1, name: "John" },
      { id: 2, name: "Jane" }
    ];
    
    render(<UserList users={mockUsers} />);
    
    await userEvent.type(screen.getByPlaceholderText(/search/i), "John");
    expect(screen.getByText("John")).toBeInTheDocument();
    expect(screen.queryByText("Jane")).not.toBeInTheDocument();
  });
});

Для целого feature (страница, сложный сценарий):

// Писать: E2E тест
// Почему: нужно проверить весь процесс, включая API и навигацию

test("user can create post and see it in feed", async ({ page }) => {
  await page.goto("/");
  await page.click("button:has-text(\"New Post\")");
  await page.fill("[name=\"title\"]", "My Post");
  await page.fill("[name=\"content\"]", "Content here");
  await page.click("button:has-text(\"Publish\")");
  
  expect(page.locator("text=My Post")).toBeVisible();
});

Правило 70/20/10

  • 70% unit тестов (быстрые, дешёвые, много деталей)
  • 20% integration тестов (проверка взаимодействия)
  • 10% E2E тестов (критичные сценарии)

Checklist при написании теста

  1. Какой компонент/функцию тестирую?
  2. Зависит ли от других компонентов?
  3. Нужен ли mock API?
  4. Проверяю ли пользовательский сценарий?

Если ответы на 2-4 "нет" -> Unit тест Если "да" на 2-3 -> Integration тест Если "да" на 4 -> E2E тест