smithery/masanao-ohba

react-testing-standards

Invoke when test-developer writes tests for React 19 components. Provides React Testing Library patterns, component rendering strategies, user event simulation, async testing patterns, and mock setup for hooks, context, and API calls.

Installation

$ npx skills add smithery/masanao-ohba --skill react-testing-standards

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/masanao-ohba.

npx skills add smithery/masanao-ohba

Browse all from smithery/masanao-ohba

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 9,016 B
  • docs SUMMARY.md 105 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

React Testing Standards

Testing Philosophy

Guiding Principles

  • Test user behavior, not implementation details
  • Tests should resemble how users interact with your app
  • Query by accessible roles and labels, not test IDs
  • Integration tests over unit tests when practical
  • Test the component contract, not internal state

What to Test

Priority High:

  • User interactions (clicks, typing, navigation)
  • Conditional rendering based on props/state
  • API integration and data fetching
  • Form submissions and validation
  • Error states and error recovery

Priority Medium:

  • Accessibility features
  • Loading states
  • Edge cases and boundary conditions

Avoid Testing:

  • Implementation details (state variable names)
  • Third-party library internals
  • CSS styling (use visual regression tests)
  • Framework behavior (React itself)

React Testing Library

Query Priority

1. Accessible Queries (Most Preferred):

  • getByRole - Most preferred
  • getByLabelText - For form elements
  • getByPlaceholderText - Alternative for inputs
  • getByText - For non-interactive elements
  • getByDisplayValue - For current input values

2. Semantic Queries:

  • getByAltText - For images
  • getByTitle - For title attributes

3. Test IDs (Last Resort):

  • getByTestId - Only when element has no accessible role

Query Variants

Variant Behavior
getBy Throws error if not found - for elements that must exist
queryBy Returns null if not found - for asserting non-existence
findBy Returns promise - for async elements that appear later

User Interactions

Library: Use @testing-library/user-event, not fireEvent

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('user can type in input', async () => {
  const user = userEvent.setup();
  render(<SearchBox />);

  const input = screen.getByRole('textbox');
  await user.type(input, 'Hello');

  expect(input).toHaveValue('Hello');
});

Common Interactions:

  • user.click() - Click elements
  • user.type() - Type in inputs
  • user.clear() - Clear input values
  • user.selectOptions() - Select dropdown options
  • user.upload() - Upload files

Test Structure

Anatomy

import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ComponentName } from './ComponentName';

describe('ComponentName', () => {
  test('describes expected behavior', async () => {
    // Arrange - Set up test data and render
    const user = userEvent.setup();
    render(<ComponentName prop="value" />);

    // Act - Perform user interactions
    const button = screen.getByRole('button', { name: /click me/i });
    await user.click(button);

    // Assert - Verify expected outcomes
    expect(screen.getByText(/success/i)).toBeInTheDocument();
  });
});

Best Practices

  • One logical assertion per test
  • Descriptive test names (what behavior is tested)
  • Arrange-Act-Assert pattern
  • Avoid beforeEach for test setup (makes tests less clear)
  • Use async/await for user interactions

Testing Patterns

Component Rendering

Basic:

test('renders with correct props', () => {
  render(<UserCard name="John" email="[email protected]" />);

  expect(screen.getByText('John')).toBeInTheDocument();
  expect(screen.getByText('[email protected]')).toBeInTheDocument();
});

Conditional:

test('shows loading state', () => {
  render(<DataDisplay isLoading={true} />);

  expect(screen.getByRole('progressbar')).toBeInTheDocument();
});

User Interactions

Button Click:

test('increments counter on click', async () => {
  const user = userEvent.setup();
  render(<Counter />);

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

  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

Form Submission:

test('submits form with user data', async () => {
  const handleSubmit = jest.fn();
  const user = userEvent.setup();
  render(<LoginForm onSubmit={handleSubmit} />);

  await user.type(screen.getByLabelText(/email/i), '[email protected]');
  await user.type(screen.getByLabelText(/password/i), 'password123');
  await user.click(screen.getByRole('button', { name: /submit/i }));

  expect(handleSubmit).toHaveBeenCalledWith({
    email: '[email protected]',
    password: 'password123',
  });
});

Async Operations

Data Fetching:

test('displays fetched data', async () => {
  render(<UserList />);

  // Wait for loading to finish
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  // Wait for data to appear
  const users = await screen.findAllByRole('listitem');
  expect(users).toHaveLength(3);
});

With waitFor:

test('shows success message after submission', async () => {
  const user = userEvent.setup();
  render(<ContactForm />);

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

  await waitFor(() => {
    expect(screen.getByText(/thank you/i)).toBeInTheDocument();
  });
});

Error Handling

test('displays error message on failure', async () => {
  // Mock API to return error
  jest.spyOn(api, 'fetchUser').mockRejectedValue(new Error('Failed'));

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

  const errorMessage = await screen.findByText(/failed to load/i);
  expect(errorMessage).toBeInTheDocument();
});

Mocking

External Dependencies

API Calls:

// Mock API module
jest.mock('@/lib/api', () => ({
  fetchUsers: jest.fn(),
}));

test('renders users from API', async () => {
  const mockUsers = [{ id: 1, name: 'Alice' }];
  fetchUsers.mockResolvedValue(mockUsers);

  render(<UserList />);

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

React Query:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

function renderWithQueryClient(ui: React.ReactElement) {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
    },
  });

  return render(
    <QueryClientProvider client={queryClient}>
      {ui}
    </QueryClientProvider>
  );
}

What NOT to Mock

Avoid:

  • React hooks (useState, useEffect, etc.)
  • Component implementation details
  • Child components (test integration instead)

Acceptable:

  • External API calls
  • Browser APIs (localStorage, fetch)
  • Third-party libraries with side effects
  • Date/time functions (Date.now())

Accessibility Testing

Practices

  • Query by role (getByRole) enforces ARIA compliance
  • Test keyboard navigation
  • Verify focus management
  • Check alt text on images
test('button is keyboard accessible', async () => {
  const user = userEvent.setup();
  render(<Dialog />);

  // Tab to button
  await user.tab();
  expect(screen.getByRole('button')).toHaveFocus();

  // Activate with Enter
  await user.keyboard('{Enter}');
  expect(screen.getByRole('dialog')).toBeInTheDocument();
});

Test Organization

File Structure

  • ComponentName.test.tsx - Component tests
  • utils.test.ts - Utility function tests
  • tests/ directory - Alternative structure

Describe Blocks

describe('LoginForm', () => {
  describe('validation', () => {
    test('shows error for invalid email', () => {});
    test('shows error for short password', () => {});
  });

  describe('submission', () => {
    test('calls onSubmit with form data', () => {});
    test('shows success message after submit', () => {});
  });
});

Coverage Guidelines

Requirements

Area Coverage
Critical paths 100%
Components 80%+
Utilities 90%+

What to Prioritize

  • User-facing features
  • Business logic and calculations
  • Error handling paths
  • Form validation

Acceptable Gaps

  • Pure presentation components
  • Third-party library wrappers
  • Type definitions

Common Pitfalls

Avoid

  • Testing implementation details (state names, effect calls)
  • Snapshot tests for everything (brittle and uninformative)
  • Using getByTestId as primary query method
  • Not awaiting async operations
  • Asserting on intermediate loading states

Instead

  • Test public API and user-visible behavior
  • Snapshots only for static content that changes infrequently
  • Query by role/label for accessibility
  • Always await user events and async queries
  • Assert on final rendered state

CI Integration

Requirements

  • All tests must pass before merge
  • Coverage reports generated and tracked
  • Tests run on every pull request
  • Fast test execution (< 5 minutes for unit/integration)