React Example

Testing React Applications

Master testing techniques for React applications

Unit Testing

Testing individual components with Jest

Component Test

import { render, screen } from '@testing-library/react';
import Counter from './Counter';

describe('Counter Component', () => {
  test('renders initial count of zero', () => {
    render();
    const countElement = screen.getByText(/count: 0/i);
    expect(countElement).toBeInTheDocument();
  });

  test('increments count when button is clicked', () => {
    render();
    const button = screen.getByRole('button', { name: /increment/i });
    fireEvent.click(button);
    expect(screen.getByText(/count: 1/i)).toBeInTheDocument();
  });
});

Hook Test

import { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';

describe('useCounter Hook', () => {
  test('should increment counter', () => {
    const { result } = renderHook(() => useCounter());

    act(() => {
      result.current.increment();
    });

    expect(result.current.count).toBe(1);
  });

  test('should decrement counter', () => {
    const { result } = renderHook(() => useCounter());

    act(() => {
      result.current.decrement();
    });

    expect(result.current.count).toBe(-1);
  });
});

Key Points

  • Test component rendering
  • Test user interactions
  • Test custom hooks
  • Use act() for state updates

Integration Testing

Testing component interactions

Form Submission Test

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import LoginForm from './LoginForm';

describe('LoginForm', () => {
  test('submits form with user credentials', async () => {
    const onSubmit = jest.fn();
    render();

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: 'test@example.com' }
    });

    fireEvent.change(screen.getByLabelText(/password/i), {
      target: { value: 'password123' }
    });

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

    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledWith({
        email: 'test@example.com',
        password: 'password123'
      });
    });
  });

  test('displays validation errors', async () => {
    render();
    
    fireEvent.click(screen.getByRole('button', { name: /submit/i }));

    expect(await screen.findByText(/email is required/i)).toBeInTheDocument();
    expect(await screen.findByText(/password is required/i)).toBeInTheDocument();
  });
});

Key Points

  • Test form submissions
  • Test validation logic
  • Mock API calls
  • Test async operations

E2E Testing

End-to-end testing with Cypress

User Flow Test

describe('Authentication Flow', () => {
  beforeEach(() => {
    cy.visit('/');
  });

  it('should login successfully', () => {
    cy.get('[data-cy=login-email]')
      .type('user@example.com');
    
    cy.get('[data-cy=login-password]')
      .type('password123');
    
    cy.get('[data-cy=login-submit]')
      .click();
    
    cy.url()
      .should('include', '/dashboard');
    
    cy.get('[data-cy=welcome-message]')
      .should('contain', 'Welcome back');
  });

  it('should show error for invalid credentials', () => {
    cy.get('[data-cy=login-email]')
      .type('invalid@example.com');
    
    cy.get('[data-cy=login-password]')
      .type('wrongpassword');
    
    cy.get('[data-cy=login-submit]')
      .click();
    
    cy.get('[data-cy=error-message]')
      .should('be.visible')
      .and('contain', 'Invalid credentials');
  });
});

API Mocking

describe('Dashboard', () => {
  beforeEach(() => {
    cy.intercept('GET', '/api/user/profile', {
      fixture: 'profile.json'
    }).as('getProfile');

    cy.intercept('GET', '/api/dashboard/stats', {
      fixture: 'stats.json'
    }).as('getStats');

    cy.visit('/dashboard');
  });

  it('should display user data', () => {
    cy.wait('@getProfile');
    cy.get('[data-cy=user-name]')
      .should('contain', 'John Doe');
    
    cy.wait('@getStats');
    cy.get('[data-cy=total-orders]')
      .should('contain', '150');
  });

  it('should handle API errors', () => {
    cy.intercept('GET', '/api/dashboard/stats', {
      statusCode: 500,
      body: { error: 'Server error' }
    }).as('getStatsError');

    cy.visit('/dashboard');
    cy.get('[data-cy=error-message]')
      .should('be.visible');
  });
});

Key Points

  • Test complete user flows
  • Mock API responses
  • Test error scenarios
  • Visual regression testing