ワンクリックで
testing
Write tests with Vitest and Testing Library. Use when creating tests, writing specs, mocking, or setting up test coverage.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Write tests with Vitest and Testing Library. Use when creating tests, writing specs, mocking, or setting up test coverage.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Git workflow for branches, commits, and PRs. Use for commit, branch, pr, pull request, conventional, push, feat, fix, chore, merge, rebase
Storybook stories and interaction tests. Use for story, stories, storybook, chromatic, visual test, interaction test, play function, component test
TypeScript and unicorn linting patterns. Use for typescript, array, for-of, reduce, forEach, throw, catch, modern js, es modules, string, number, error handling
Zod v4 schema validation. Use for zod, schema, validation, parse, safeParse, infer, coerce, transform, refine, z.object, z.string, z.email, z.url
Better Auth authentication. Use for auth, login, logout, session, user, signup, register, protect, middleware, password, oauth, social
Drizzle ORM + PostgreSQL database layer. Use for db, database, query, schema, table, migrate, sql, postgres, drizzle, model, relation
| name | testing |
| description | Write tests with Vitest and Testing Library. Use when creating tests, writing specs, mocking, or setting up test coverage. |
pnpm test # Run all
pnpm test:watch # Watch mode
pnpm test:coverage # With coverage
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { Button } from '@oakoss/ui';
describe('Button', () => {
it('renders with text', () => {
render(<Button>Click me</Button>);
expect(
screen.getByRole('button', { name: /click me/i }),
).toBeInTheDocument();
});
});
import userEvent from '@testing-library/user-event';
it('handles click', async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(<Button onPress={onClick}>Click</Button>);
await user.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalled();
});
getByRole - Accessible elementsgetByLabelText - Form inputsgetByText - Text contentgetByTestId - Last resortconst mockFn = vi.fn().mockReturnValue('result');
vi.mock('@oakoss/ui', () => ({
Button: vi.fn(({ children }) => <button>{children}</button>),
}));
const spy = vi.spyOn(utils, 'cn');
await screen.findByText('Loaded');
await waitFor(() => {
expect(screen.getByRole('list')).toHaveTextContent('Item');
});
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
function createTestQueryClient() {
return new QueryClient({
defaultOptions: {
queries: { retry: false, gcTime: 0 },
mutations: { retry: false },
},
});
}
function renderWithQuery(ui: React.ReactElement) {
const queryClient = createTestQueryClient();
return render(
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>,
);
}
it('loads and displays data', async () => {
vi.mock('@/lib/api', () => ({
getPosts: vi.fn().mockResolvedValue([{ id: '1', title: 'Test' }]),
}));
renderWithQuery(<PostList />);
expect(await screen.findByText('Test')).toBeInTheDocument();
});
import userEvent from '@testing-library/user-event';
it('validates and submits form', async () => {
const user = userEvent.setup();
const onSubmit = vi.fn();
render(<LoginForm onSubmit={onSubmit} />);
await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password123');
await user.click(screen.getByRole('button', { name: /submit/i }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123',
});
});
});
it('shows validation errors', async () => {
const user = userEvent.setup();
render(<LoginForm />);
await user.click(screen.getByRole('button', { name: /submit/i }));
expect(await screen.findByText(/email is required/i)).toBeInTheDocument();
});
import { RouterProvider, createMemoryHistory } from '@tanstack/react-router';
function renderWithRouter(ui: React.ReactElement, { route = '/' } = {}) {
const history = createMemoryHistory({ initialEntries: [route] });
const router = createRouter({ routeTree, history });
return render(<RouterProvider router={router}>{ui}</RouterProvider>);
}
it('navigates on click', async () => {
const user = userEvent.setup();
renderWithRouter(<Navigation />, { route: '/' });
await user.click(screen.getByRole('link', { name: /about/i }));
expect(window.location.pathname).toBe('/about');
});
vi.mock('@/lib/server-functions', () => ({
getUser: vi.fn(),
updateUser: vi.fn(),
}));
import { getUser, updateUser } from '@/lib/server-functions';
beforeEach(() => {
vi.mocked(getUser).mockResolvedValue({ id: '1', name: 'Test User' });
});
it('loads user data', async () => {
render(<UserProfile userId="1" />);
expect(await screen.findByText('Test User')).toBeInTheDocument();
expect(getUser).toHaveBeenCalledWith({ data: { id: '1' } });
});
| Mistake | Correct Pattern |
|---|---|
Using getBy for async content | Use findBy or waitFor for async |
| Testing implementation details | Test behavior, not internal state |
Using getByTestId first | Prefer getByRole, getByLabelText |
Missing userEvent.setup() | Always call userEvent.setup() first |
| Not wrapping in act | Use userEvent which handles this |
| Mocking too much | Only mock external dependencies |
| Not cleaning up mocks | Use vi.clearAllMocks() in beforeEach |
| Shared QueryClient in tests | Create fresh QueryClient per test |
| Retry enabled in test queries | Set retry: false in test QueryClient |
| Testing third-party components | Trust the library, test your usage |
Explore agentTask agentcode-reviewer agent| Task | Skill |
|---|---|
| Query testing patterns | tanstack-query |
| Form testing patterns | tanstack-form |
| Router testing patterns | tanstack-router |
| Error boundary testing | error-boundaries |
| Integration flows | integration-patterns |