Галоўная / Артыкулы

Анті-шаблоны тэставання у React, які ствараюць хыбную адказку ў 2026 годзе

Дазвольце пазнакоміць вас з дзесятьма распашчытнымі памылкамі прабавання ў React — ад неправильнага выкарыстоўвання fireEvent да проблем з імітаваннем сецьяны — якія дазваляюць несправным функцыям праскочыць праз апошню, усе перажываючую сэтку тэстаў.

Выдавец: REACTAPP.TOP14 хв чытання3125 слоў

Крыніцы: 10 React Testing Mistakes That Are Secretly Making Your Tests Useless in 2026

У вашай пакетаце тэстоў усе праходзіць успешна, кожны пераконтроль выказваецца як «зелены», і вы з задоволеннем запускаеце продакшн-версію. Але потым черга падтрымкі заполняецца: кантактны кнопка на мобайльных прыстроях няўыразна не работае, экран аблогавання выдае памылку, калі API адпавядае медленна, а корыстнік, які выкарыстоўвае клавіатуру, вовсе не можа скорыстацца меню навігацыі. Нічога з таго не было зафіксавана ў вашых тэстах. Тады што ж сталося?

До 2026 года, калі React 19 працуе разам з React Compiler і Testing Library v16, адстань межаў пакетацыі тэстоў, якая проста праходзіць, і тойя, якая насправды захоўвае вашу аплікацыю, стала большай, чым будзь-колі раней. Ёсць дзесять прычын, якія заставляюць вашы тэсты выглядаць рэшучымі, хоча ваша продакшн-аплікацыя ўсё равно таямна ламаецца.

1. Тэставанне деталяў реалізацыі заместо поведэння корыстніка

Это класычныя працыбы прабавання React, і яны продовжваюць выконваліцца ўсё час. Компанента атрыбуецца, яе внутрэшняя структура паддаецца аналізу, а тэсты пераканальваюцца ў чымось уродзе таго, чы не быў вызваны useState або чы не была перадана певная проп. Гэта не пераканальванне таго, што фактычна бачыць корыстнік — гэта пераканальванне таго, як разработчык яе пабудаваў.

// ❌ Useless: Testing internals
import { render } from '@testing-library/react';
import { LoginForm } from './LoginForm';
test('sets isSubmitting to true on submit', () => {
  const wrapper = render(<LoginForm />);
  const instance = wrapper.container.firstChild; // Reaching into internals

  // This tests React, not your app
  expect(instance).toHaveProperty('isSubmitting');
});

Такі тэст є кранкім і беспраганым: перайменаваць зменную, замяніць useState на useReducer або вынесці логіку ў спецыяльны хук — і ён перестае працаваць, нават якщо нічога не змянілася ў самым процесе заходжання. Гэты тэст не дае жадных сведчынкаў пра тое, чы можа людзі заходзіць у систему.

// ✅ Useful: Testing behavior
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('submits the form with user credentials', async () => {
  const handleSubmit = vi.fn();
  render(<LoginForm onSubmit={handleSubmit} />);

  const user = userEvent.setup();

  await user.type(screen.getByLabelText(/email/i), 'test@example.com');
  await user.type(screen.getByLabelText(/password/i), 'secret123');
  await user.click(screen.getByRole('button', { name: /sign in/i }));

  expect(handleSubmit).toHaveBeenCalledWith({
    email: 'test@example.com',
    password: 'secret123',
  });
});

Асалідны прынцып Testing Library застаецца актуальным і ў 2026 годзе: тэсты, якія відпаваюць реальнаму выкарыстоўванню, даюць больш правідлівага адчування паверы. Якщо безнэпакойны рефакторинг можа зламаць ваш тэст, значы вы перакананы ў неправім слое.

2. Іспытанне fireEvent заместо userEvent

fireEvent проста запускае чыстыя DOM-званні. userEvent відтворае всю ланцоўку дзеяння, якія выкаонвае рэальны чалавек. Практычна не застаўся жадання выкарыстоўваць fireEvent у тэстах компанентаў, але гэта ўсё яшчэ практыкуецца, таму што так вучылі старэйшыя інструкцыі.

// ❌ Useless: fireEvent does not simulate real interaction
test('toggles the menu', () => {
  render(<Navigation />);
  const button = screen.getByText('Menu');

  fireEvent.click(button); // Just fires a click event

  expect(screen.getByText('Dashboard')).toBeInTheDocument();
});

Проблема заключаецца у тым, што fireEvent.click ніколі не паўтарае, чы можа элемент насправды быць нажаты чалавекам. Ён можа быць выключаны, пакрыты іншым элементам або цалком недоступны за дапамогою клавіатуры. Асэрцыя успевае, хоча фактычнае функцыянаванне є недаступным.

// ✅ Useful: userEvent validates the interaction
test('toggles the menu with keyboard and mouse', async () => {
  render(<Navigation />);
  const user = userEvent.setup();
  const button = screen.getByRole('button', { name: /menu/i });

  // Verifies element is focusable and clickable
  await user.click(button);
  expect(screen.getByRole('link', { name: /dashboard/i })).toBeVisible();

  // Tests keyboard accessibility
  await user.keyboard('{Escape}');
  expect(screen.queryByRole('link', { name: /dashboard/i })).not.toBeInTheDocument();
});

userEvent спачатку пераканаецца, што цэль не ў выключаным стане і не ў режыме толькі для чытання, а таксама што яна дэйсна візуальна доступная, перш чым запускаць які-небудзь адказ, пасля чаго імітуе рэалістычную последовальнасць змян парадкурсу курсора, суперакцый клавіатуры і змян фокуса. Доступнасць больш не ёсць прыемным допамогам, і ваші тэсты должны аўтаматычна старацца прыменіць гэты прынцып.

3. Імітаванне сеансаў з’ёднання з сетю на неправильным рэвэлі

Спадзяймося, вы тэстуеце компонент, які запошчае даны з API. Швайны спосаб — імітаваць fetch або axios.get працэс у самым файле тэсту. Тэст праходзіць успешна. Пасля чаго каманда, якая працуе з бэкендам, перайменавае полье з userName на username, працэс у рэальных умовах зупіняецца, а ваша сукупнасць тэстаў нічога не зазначае.

// ❌ Useless: Mocking fetch directly
test('displays user profile', async () => {
  global.fetch = vi.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve({ userName: 'Alice' }), // Drifts from reality
    })
  );

  render(<UserProfile userId="123" />);

  expect(await screen.findByText('Alice')).toBeInTheDocument();
});

Проблема тут у тым, што вы перавяроўваете данні за дапамой вымышленага API, а не рэальнага. Стабб не перакантролюе заголовкі, не перапрацоўвае структуру URL і не імітуюць затрымкі чы розбіяння запытоў. Ён дае вам хыбную пэўнасць, тады калі рэальная інтеграцыя паступова выходзіць з синхрону.

// ✅ Useful: Mocking at the network layer with MSW
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
  http.get('/api/users/:id', ({ params }) => {
    return HttpResponse.json({ username: 'Alice', id: params.id });
  })
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('displays user profile from API', async () => {
  render(<UserProfile userId="123" />);

  // Tests the actual component + network layer together
  expect(await screen.findByText(/alice/i)).toBeInTheDocument();
  expect(screen.getByText(/id: 123/i)).toBeInTheDocument();
});

MSW працюе на межы сеті, таму ваш компонент выканаўць той самы шлях вызову fetch, які ён бы выкарыстаў у рэальных умовах. Калі змянюецца контракт API, вы паспеліваеце працэс MSW ў аднам месца, і кожны тест, які запускаецца да гэтага канцэнтра, автоматычна адбівае новую рэальнасць. Да 2026 года MSW 2.x стаў стандартным выборам, а прыменэнне стаббаў да fetch безпосередна ёсць сваеўразумным накопленнем тэхнічнага боргу.

4. Апоўзанне на снімкі стану як замену для перагляду регрэсій

Тэсты на статычныя кадры ўражаюць самэй таму, што ад вас трэбуецца мінімум. Вы ствараеце ўтворэнне, фіксуеце отраслевы код HTML і падаеце його на перагляд. Чырэх месцаў пазней дизайнер правіляе модуль CSS, статычны кадр перестае працаваць, і хтось затверджвае новы кадр проста нажмуўшы клавішу, так і не чытання таго, што змянілася.

// ❌ Useless: Snapshot as a crutch
test('renders correctly', () => {
  const { container } = render(<Card title="Hello" />);
  expect(container.firstChild).toMatchSnapshot();
});

Проблема ў тым, што ніхто насправдзе не пераглядае 400-радковыя разніцы серыяванага маркапу. Статычны кадр пазначае, што што-небудзь змянілася, але не можа сказаць, чы то была безнэпакойная правіка дизайна чы зламаная структура. Це шум, выдаваемы за сітку безпекі.

// ✅ Useful: Targeted assertions
test('renders card with accessible title and action', () => {
  render(<Card title="Hello" onAction={() => {}} />);

  expect(screen.getByRole('heading', { name: /hello/i })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /action/i })).toBeEnabled();
  expect(screen.getByRole('article')).toHaveAttribute('aria-labelledby');
});

Запісваючы цялевыя асерцыі, мы точна паказвам, якую працэздатнасць обяўляе компонент. Якщо структура маркапа змінюецца, але базаваны контракт доступнасці застаецца чынным, тэст продовжвае праходзіць. Якщо роль загалоўка випадкова зникае, мы отрымываем точную паведамленне пра абэрасію, якае вказывае на справжню проблему. До 2026 года варта цэлкам выдаліць вашы файлы-скріншоты і заменіць іх асерцыямі, якіе выражаюць вашы рэальныя аспактаванні.

5. Выбіранне элементаў выключна за дапамогою data-testid

Вываранне атрыбута data-testid на кожны элемент здаецца безпечным, непраблэмным варыянтом — запит завжды знаходзіць свою мету. Але селектар data-testid нічога не падтверджвае ў чысці доступнасці, семантыкі чыя то, як фактычны корыстнік воспрімае інтэрфейс. Воно толькі падтверджвае, што разработчык запамятаў увести конкрэтную строчку.

// ❌ Useless: Brittle, tests nothing about accessibility
function Form() {
  return (
    <div data-testid="form-container">
      <input data-testid="email-input" />
      <button data-testid="submit-btn">Submit</button>
    </div>
  );
}
test('submits form', async () => {
  render(<Form />);
  await userEvent.type(screen.getByTestId('email-input'), 'test@example.com');
  await userEvent.click(screen.getByTestId('submit-btn'));
});

Такі тэсты з задоволеннем працуюю нават тады, калі у вхідных данных не ёсць атрыбута з пазначэнням, калі „кнопка“ на самай працоўцы ёсць проста стаязованы элемент div, а корыстнік з чытальнікам экрана будзе абсалютна заплутаны. Фактычна, такі падход паўтарае адмову ў правільнасці бракучага маркапаў.

// ✅ Useful: Semantic queries enforce accessibility
function Form() {
  return (
    <form aria-label="Login form">
      <label htmlFor="email">Email address</label>
      <input id="email" type="email" />
      <button type="submit">Submit</button>
    </form>
  );
}
test('submits login form', async () => {
  render(<Form />);
  const user = userEvent.setup();

  // These queries fail if the elements are not accessible
  await user.type(screen.getByLabelText(/email address/i), 'test@example.com');
  await user.click(screen.getByRole('button', { name: /submit/i }));

  expect(screen.getByRole('form', { name: /login form/i })).toBeInTheDocument();
});

Рэкамендаваныя парадкі выкарыстоўвання запитоў у Testing Library не ёсць вялікай спрэй: getByRole, getByLabelText і getByPlaceholderText спрыяюць стварэнню маркапаў, якія ёсць практычна доступныя. Якщо единственны спосаб з’явіцца да элемента — через data-testid, то гэта зазвычай ўскладненне таго, што сам компонент не ёсць доступным. З урастаннем справ пра доступнасць, керованных AI, да 2026 года, гэта перастае быць простаю альтернатывай тэставанню і стае справжнім правовым рызыкам.

6. Айхання чакання на асінхронную працу

Практычна ўсё ў React выконваецца з адзінаковым запазненням. Змены стану агрупаваюцца, эфекты запускаюцца пасля таго, як браузер намалюе елементы, а запуск завантажэння дадзеных відбываецца толькі пасля наступнага цыклу. Якщо тэст не чакае належным чынам, пакі ўсе гэта завершыцца, його пераканання выконваюцца занадта рана, і тэст прыймаеся, не пераканавшысь у чым-небудзь значным.

// ❌ Useless: Missing await on async operations
test('shows search results', () => {
  render(<Search />);

  userEvent.type(screen.getByRole('searchbox'), 'react');
  userEvent.keyboard('{Enter}');

  // This runs before the fetch resolves
  expect(screen.getByText('React Testing')).toBeInTheDocument();
});

Залежна ад кэшавання чыста часу, такі тэст можа прыймацца з везенням чыста паспаўляць неспадзевана. Гэта, мабыць, горш за тое, калі тэста вовсе няма, адколі гэта стварае хыбная адчуванне безпекі.

// ✅ Useful: Explicitly await async states
test('shows search results after typing', async () => {
  render(<Search />);
  const user = userEvent.setup();

  await user.type(screen.getByRole('searchbox'), 'react');
  await user.keyboard('{Enter}');

  // findBy queries wait for the element to appear
  expect(await screen.findByText('React Testing')).toBeInTheDocument();

  // Ensure loading state was shown and removed
  expect(screen.queryByRole('status', { name: /loading/i })).not.toBeInTheDocument();
});

Праўячыся да 2026 года, з хуком use у React 19 і шырэйшым выкарыстаннем меж Suspense, асінхронныя патэраны з’яўляюцца практычна скроз. Кожная взаімадзея, якая запускае апдэйт стану, пераход чы ўзначальненне запыту да сеті, павінна быть належным чынам ачакана. Выкорыстоўваюце запыты findBy, калі элемент ап’являецца асінхронна, waitFor — для умов, якія выходзяць за межы простых пераканальняў пра наявнасць, і стараецеся, каб кожны вызов userEvent быў ачаканы.

7. Тэставанне компонентаў у ізоляцыі ад их рэальнай среды

Часта бывае, што компонент адрасаваецца цэлкам самостоятельна — без рутера, без прадаўцы тэмы, без кліента запытоў, без контексту аутанацыі — тэст праходзіць, але потым той самы компонент перастаёў працаваць у рэальных умовах, таму што ён таямна завісіў ад контексту, які тэст ніколі не задаваў.

// ❌ Useless: Missing app context
test('renders dashboard', () => {
  render(<Dashboard />);
  expect(screen.getByText('Welcome')).toBeInTheDocument();
});

Эты тэст не дае многа інфармацыі, калі ўважваць, што Dashboard верагатна выкарыстоўвае такія хукі, як useQuery, useAuth чы useNavigate. Без адпаведных прадастаў вы на самай працоўваеце не сам компонент — вы тэстуеце яго уявную версію, якая не існуе ў вашай рэальной аплікацыі.

// ✅ Useful: Custom render with all providers
import { render as rtlRender } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
import { AuthProvider } from './contexts/AuthContext';
function render(ui, { route = '/', ...options } = {}) {
  window.history.pushState({}, 'Test page', route);

  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  const Wrapper = ({ children }) => (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>
        <AuthProvider>
          {children}
        </AuthProvider>
      </BrowserRouter>
    </QueryClientProvider>
  );

  return rtlRender(ui, { wrapper: Wrapper, ...options });
}
// Now your tests run in an environment that matches production
test('renders dashboard for authenticated user', async () => {
  render(<Dashboard />, { route: '/dashboard' });

  expect(await screen.findByText(/welcome back/i)).toBeInTheDocument();
});

Услядзе за тым, як сучасныя архітектуры становяцца все бол залежнымі ад контэксту, а таксама завдзяк популярнасці слоёў кэшавання, такіх як React Query, стварэнне спяльнага персональнага памочніка для рэндарування да 2026 году перастае быць проста практычным рашэнням — ён становіцца асновай всей вашай системы. Кожны тэст павінен працаваць з тым самым наборам прадастаў, якія фактычна выкарыстоўваецца ў вашай продакшн-аплікацыі.

8. Тэставанне толькі успешнага шляху

Перакантраванне тагу, каб форма была адправлена з правільнымі дадзеннямі, каб спіс паказаўся, калі API вяртае дадзеныя, або каб кнопка рэагавала, калі ёй даўна разрешэнне, сама па сабе не ёсць памылкай — але якщо зупініцца тут, ваша паверхносць тэстав застанецца чырвоначальна непашчэрпнай, а непашчэрпныя тэсты перастаюць працаваць саме тады, калі выйнаюць адказы.

// ❌ Useless: Only the sunny day scenario
test('displays products', async () => {
  server.use(
    http.get('/api/products', () => HttpResponse.json([{ id: 1, name: 'Widget' }]))
  );

  render(<ProductList />);
  expect(await screen.findByText('Widget')).toBeInTheDocument();
});

Такія тэсты нічога не кажуць пра адказ 500, порожній набор рэзультатаў, запыт, які выйшаў за час, або прычыну, калі корыстнік адправляе некоректныя дадзеныя. Значны частака рэальных багоў у прыменні — часта ацэнваных у адзіннаццаць вясоц — хаваецца самэ ў такіх крайніх ситуацыях.

// ✅ Useful: Testing states that actually happen to users
test('displays empty state when no products exist', async () => {
  server.use(http.get('/api/products', () => HttpResponse.json([])));

  render(<ProductList />);
  expect(await screen.findByText(/no products available/i)).toBeInTheDocument();
});
test('displays error state when API fails', async () => {
  server.use(
    http.get('/api/products', () => new HttpResponse(null, { status: 500 }))
  );

  render(<ProductList />);
  expect(await screen.findByRole('alert')).toHaveTextContent(/failed to load/i);
});
test('prevents submission with invalid email', async () => {
  render(<NewsletterForm />);
  const user = userEvent.setup();

  await user.type(screen.getByLabelText(/email/i), 'not-an-email');
  await user.click(screen.getByRole('button', { name: /subscribe/i }));

  expect(screen.getByRole('alert')).toHaveTextContent(/valid email required/i);
  expect(screen.getByRole('button')).toBeDisabled(); // Submit blocked
});

Калі формальныя дзеянні ў React 19 і оптымістычныя адкантраванні UI стаюць стандартамі, перакантраванне тагу, як вашы компоненты працуюць у станах адказоў і сцэнарыях вярнення да пачатку, стае ўсё важлівейшым. Корыстнікі неабходна стануць супрацоўваць з адказамі, і ваша сукупнасць тэстав павінна паказаць, што такія ситуацыі дзе-нейкі чынам рашуюцца.

9. Дазвола дзяржавнаму стану прабіваць між тэст-кейсамі

Спяшчаны глобальны стан ўсё чащэ стварае проблемы ў наборах тэстаў. Хранілішча типу Zustand можа перадаваць значэнні з адного тэста на іншы. Кеш TanStack Query можа заставаліць старыя данні. window.location, які быў падменены ў аднам файле, можа тыха паўліваць на наступны файл. Рэзультатам яе ёсць набор тэстаў, у яком тэсты праходзяць самастоятельна, але збіваюцца пад час спраўлення ўсіх разам — або, што яшчэ болей плутаюча, праходзяць у рамках цэлага спраўлення, але збіваюцца пад час ізольаванага выконання.

// ❌ Useless: Polluted global state
import { useAuthStore } from './store';
test('logs in user', () => {
  useAuthStore.getState().setUser({ id: 1, name: 'Alice' });
  render(<Profile />);
  expect(screen.getByText('Alice')).toBeInTheDocument();
});
// This test might fail depending on run order
test('shows guest view', () => {
  render(<Profile />);
  // If the previous test ran first, Alice is still logged in
  expect(screen.getByText(/sign in/i)).toBeInTheDocument();
});

Паслягучыя наследкі — гэты набор тэстаў становіцца нестабільным, повольным і ненадзеяным. Калі такое выйдзе, інжынеры перестаюць даваць веру помилкам CI, а тэсты ператвараюцца на фонавы шум, які ніхто не дакладвае.

// ✅ Useful: Isolated state for every test
import { act } from '@testing-library/react';
beforeEach(() => {
  // Reset Zustand store
  act(() => {
    useAuthStore.setState({ user: null, isAuthenticated: false });
  });

  // Clear React Query cache
  queryClient.clear();

  // Reset MSW handlers
  server.resetHandlers();

  // Reset mocks
  vi.clearAllMocks();
});
test('logs in user', async () => {
  // Test with explicit setup
  act(() => {
    useAuthStore.getState().setUser({ id: 1, name: 'Alice' });
  });

  render(<Profile />);
  expect(screen.getByText('Alice')).toBeInTheDocument();
});
test('shows guest view', () => {
  // Guaranteed clean state
  render(<Profile />);
  expect(screen.getByText(/sign in/i)).toBeInTheDocument();
});

Праўячыся да 2026 года, калі будуць атомны бібліятэкі станоў і спяльныя кешы па всюдзе, вельмі важліва спрыяць ізоляцыі тэстаў як часткі архітектуры, а не як дадатковай меры. Кожны тэст павінен ствараць свой скарысты контэкст, пераканацца ў правільнай роботе і пасля цього знишчыць усё. Хорашы крэтар дыягназу: якщо ваша серія тэстаў не можа працаваць у абыяковай парадзе, значы ў вашай стратэгіі ізоляцыі є проблема.

10. Асерцыі, якія ніколі не можу збыцца

Можа, найхітрэйшая памылка — це напісанне тэста, які выглядае правільным, але структурна не можа выявіць баг. Гэта зазвычай трапляецца, калі асерцыя нацэлена на неправильны элемент, калі пропускаецца await або калі викорыстовуецца занадта паслабны мэтчер.

// ❌ Useless: Assertion that cannot fail
test('modal opens', async () => {
  render(<Modal trigger={<button>Open</button>} />);

  // getBy throws if not found, but this is not an assertion
  screen.getByRole('dialog');

  // This passes even if the modal is empty or hidden
  expect(screen.getByRole('dialog')).toBeTruthy();
});
// ❌ Useless: Missing await on expect
test('shows notification', async () => {
  render(<Notifier />);
  await userEvent.click(screen.getByRole('button'));

  // Without await, this checks before the notification appears
  expect(screen.findByText(/success/i)).toBeInTheDocument();
});
// ❌ Useless: Testing presence of truthy object
test('form is valid', () => {
  const { container } = render(<Form />);
  // container is always truthy
  expect(container).toBeTruthy();
});

Такі тэсты, можна сказаць, ўжо горшыя, чым яго абсалютная відсутнасць — яны продаюць хвілі ресурсаў CI і ствараюць у команды хыбнае пачуццё вяры, пры тым не можучы выявіць рэальную регрэсію, калі яна выйшла.

// ✅ Useful: Specific, awaited, negative assertions
test('modal opens and traps focus', async () => {
  render(<Modal trigger={<button>Open</button>} />);
  const user = userEvent.setup();

  await user.click(screen.getByRole('button', { name: /open/i }));

  // Assert visibility, not just presence
  const modal = screen.getByRole('dialog', { name: /confirm/i });
  expect(modal).toBeVisible();

  // Assert focus management
  expect(screen.getByRole('button', { name: /close/i })).toHaveFocus();

  // Assert escape key behavior
  await user.keyboard('{Escape}');
  expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
test('shows success notification after action', async () => {
  render(<Notifier />);
  const user = userEvent.setup();

  await user.click(screen.getByRole('button'));

  // await the findBy, then assert
  const notification = await screen.findByRole('alert');
  expect(notification).toHaveTextContent(/successfully saved/i);
  expect(notification).toHaveAttribute('aria-live', 'polite');
});

Урахаваючы продовжлівую спробу React Testing Library перайсці да пераканання па прынцыпах, спрямованных да корыстувальніка, у 2026 годзе апоўзанне на слабкія мэтчеры, такія як toBeTruthy чыста toBeDefined, трэба вважаць сігналам адзячання. Варта праглядаць асэрцыі на відразу видным тэкстам, атрыбутах ARIA, стане выключання і таму, што насправды атрымліваецца. Корыстны прыём: якщо асэрцыя ўсё равно праходзіць для порожньага div, яе трэба перапісаць.

Чаклетка тэставання для 2026 году

Перад злучэнням новага тэсту перагляньце гэты список:

  • [ ] Чы ён пераканваецца ў поведзенні, якое бачыць корыстувальнік, а не ў внутршней реалізацыі?
  • [ ] Чы гэта стварае адносіны з userEvent, а не з fireEvent?
  • [ ] Чы шар сеті імітаваны через MSW, а не за дапомогою простага стаба fetch?
  • [ ] Чы выконваюцься намераваныя перакананні замест таго, каб паследавацца ад знімакоў стану?
  • [ ] Чы элементы вылучаюцца па ўпадзе і абазаванні, а не па data-testid?
  • [ ] Чы всі асінхронныя крокі належна чакаюцца?
  • [ ] Чы ён адрасаваецца внутры той ж самай іерархіі прадаўцоў, якую выкарыстоўвае рэальныя прыстрой?
  • [ ] Чы там пераглядаюцься станы адказаў на бяды, порожніх дадзеных і загрузкі, а не толькі успешны шлях?
  • [ ] Чы глобальны стан перазначаецца, каб тэст могаў выконвацца ў будзь-ям порядку?
  • [ ] Чы перакананне практычна збяжыцца, якщо базовая працэса зламаецца?
  • Проста наявнасць зелёнай галочкі ў вашым наборе тэстаў не ёсць справжнім цяляй — справжня мета — здобыць можлівасць павержацца ў гэтую галочку. Усунуўшы этыя дзесяць працоў, вашы тэсты перастануць быць формальнасцю і стануць справжнім заходам безпекі, якім яны і павінны быць.