← Назад к вопросам
Как разработчик решает где писать тест?
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 при написании теста
- Какой компонент/функцию тестирую?
- Зависит ли от других компонентов?
- Нужен ли mock API?
- Проверяю ли пользовательский сценарий?
Если ответы на 2-4 "нет" -> Unit тест Если "да" на 2-3 -> Integration тест Если "да" на 4 -> E2E тест