Testy (Vitest / React Testing Library)

Frontend ZEUS testujemy Vitest-em (szybki, zgodny z Vite/ESM) i React Testing Library (RTL). Filozofia RTL: testuj to, co widzi użytkownik, nie szczegóły implementacji.

Vitest — testy logiki

Czystą logikę z lib/ testujemy jak zwykłe funkcje.

import { describe, it, expect } from "vitest";
import { formatForest } from "@/lib/format";

describe("formatForest", () => {
  it("przycina i obniża wielkość liter", () => {
    expect(formatForest("  CORP.LOCAL ")).toBe("corp.local");
  });
});

Standard ProfessNet: pliki testów leżą obok kodu jako *.test.ts / *.test.tsx. Nazwa it(...) opisuje zachowanie po polsku albo po angielsku — byle jednolicie w obrębie projektu.

React Testing Library — testy komponentów

Testujemy zachowanie z perspektywy użytkownika: co jest na ekranie, co się dzieje po kliknięciu. Selektory wybieramy po roli i tekście, nie po klasach CSS.

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ProbeTable } from "@/app/(dashboard)/probes/probe-table";

const probes = [
  { id: "1", host: "dc01.corp", forest: "corp" },
  { id: "2", host: "dc02.dmz", forest: "dmz" },
];

it("filtruje probe'y po wpisaniu w pole", async () => {
  render(<ProbeTable probes={probes} />);

  await userEvent.type(screen.getByRole("textbox"), "dmz");

  expect(screen.getByText("dc02.dmz")).toBeInTheDocument();
  expect(screen.queryByText("dc01.corp")).not.toBeInTheDocument();
});

Standard ProfessNet: priorytet selektorów to getByRolegetByLabelTextgetByText. getByTestId używamy ostatecznie. Nie testujemy stanu wewnętrznego komponentu ani nazw klas.

Mockowanie fetch i modułów

Żądania sieciowe mockujemy, żeby testy były szybkie i deterministyczne.

import { vi, beforeEach, it, expect } from "vitest";
import { getProbe } from "@/lib/probes";

beforeEach(() => {
  vi.restoreAllMocks();
});

it("zwraca sparsowany obiekt probe", async () => {
  vi.spyOn(globalThis, "fetch").mockResolvedValue(
    new Response(JSON.stringify({ id: "1", host: "dc01", forest: "corp" })),
  );

  const probe = await getProbe("1");
  expect(probe.host).toBe("dc01");
});

Konfiguracja

// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom",
    setupFiles: ["./vitest.setup.ts"],
    globals: true,
  },
});
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest"
  }
}
Rodzaj testuNarzędzieCo sprawdza
logika lib/Vitestwejście → wyjście funkcji
komponentVitest + RTLrender i interakcja jak u usera
hookRTL renderHookzwracany stan i efekty

Wskazówka: jeśli test ciągle się psuje przy refaktorze, choć aplikacja działa — prawdopodobnie testujesz implementację, nie zachowanie. Przepisz go w stylu RTL.


Vitest do logiki, RTL do komponentów, testuj zachowanie a nie wnętrzności. Taki zestaw testów przeżywa refaktory i daje pewność przy każdym PR-ze.