Post Image

Testing Styled Components with React Testing Library

By Andrew Martin on 10th March, 2025

    Testing styled components ensures your React app looks and behaves as expected. This guide explores how to set up tools like React Testing Library and jest-styled-components, write tests for styles and props, and handle themes and dynamic styles. You’ll also learn snapshot testing and best practices for maintaining clean, reliable tests. By focusing on critical styles and behaviors, you can catch issues early and keep your components consistent with your design system.

    Testing In React Tutorial – Jest and React Testing Library

    Test Environment Setup

    Setting up a proper testing environment is crucial when working with styled-components and React Testing Library. Here’s how you can get started.

    Tools You Need

    Make sure to install these dependencies:

    Package Version Purpose
    @testing-library/react ^14.0.0 Core utilities for testing React apps
    @testing-library/jest-dom ^6.1.0 Custom matchers for Jest
    styled-components ^6.0.0 CSS-in-JS library
    jest-styled-components ^7.1.1 Utilities for testing styled-components

    Use the following commands to add them to your project:

    npm install --save-dev @testing-library/react @testing-library/jest-dom jest-styled-components
    npm install styled-components
    

    Configuring Jest

    To make Jest work seamlessly with styled-components, update your Jest configuration file like this:

    module.exports = {
      setupFilesAfterEnv: [
        '@testing-library/jest-dom',
        'jest-styled-components'
      ],
      testEnvironment: 'jsdom',
      transform: {
        '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest'
      }
    };
    

    This configuration ensures Jest is ready to handle both JavaScript and TypeScript files, while also supporting styled-components for reliable style testing and snapshot comparisons.

    Basic Test Writing

    Component Render Tests

    To ensure a styled component renders correctly and applies default styles, you can write a simple test like this:

    // Button.js
    const StyledButton = styled.button`
      background: #007bff;
      color: white;
      padding: 10px 20px;
      border-radius: 4px;
    `;
    
    // Button.test.js
    import { render, screen } from '@testing-library/react';
    import { StyledButton } from './Button';
    
    test('renders button with correct default styles', () => {
      render(<StyledButton>Click me</StyledButton>);
      const button = screen.getByText('Click me');
    
      expect(button).toHaveStyleRule('background', '#007bff');
      expect(button).toHaveStyleRule('color', 'white');
    });
    

    This test ensures your component’s base styles are applied as expected.

    Style Props Tests

    To verify how styles change based on props, you can write tests like this:

    const StyledButton = styled.button`
      background: ${props => props.variant === 'primary' ? '#007bff' : '#6c757d'};
      color: white;
    `;
    
    test('button applies correct styles based on variant prop', () => {
      const { rerender } = render(<StyledButton variant="primary">Primary</StyledButton>);
    
      expect(screen.getByText('Primary')).toHaveStyleRule('background', '#007bff');
    
      rerender(<StyledButton variant="secondary">Secondary</StyledButton>);
      expect(screen.getByText('Secondary')).toHaveStyleRule('background', '#6c757d');
    });
    

    This approach ensures your component adapts its styles based on the props provided.

    Dynamic Style Tests

    Dynamic style testing focuses on state-driven changes and more complex scenarios. Here’s an example:

    const DynamicInput = styled.input`
      border: 2px solid ${props => props.isValid ? 'green' : 'red'};
      background: ${props => props.disabled ? '#f5f5f5' : 'white'};
    `;
    
    test('input applies correct styles based on validation and disabled state', () => {
      const { rerender } = render(<DynamicInput isValid={true} />);
    
      let input = screen.getByRole('textbox');
      expect(input).toHaveStyleRule('border', '2px solid green');
      expect(input).toHaveStyleRule('background', 'white');
    
      rerender(<DynamicInput isValid={false} disabled={true} />);
      input = screen.getByRole('textbox');
      expect(input).toHaveStyleRule('border', '2px solid red');
      expect(input).toHaveStyleRule('background', '#f5f5f5');
    });
    

    When testing dynamic styles, focus on the key changes that impact functionality and user experience. The toHaveStyleRule matcher from jest-styled-components is a great tool for verifying these transformations.

    sbb-itb-f6354c6

    Advanced Testing Methods

    Theme Provider Tests

    You can test ThemeProvider components like this:

    const theme = {
      colors: {
        primary: '#0052cc',
        secondary: '#6554c0'
      },
      spacing: {
        small: '8px',
        medium: '16px'
      }
    };
    
    const ThemedButton = styled.button`
      background: ${props => props.theme.colors.primary};
      padding: ${props => props.theme.spacing.medium};
    `;
    
    test('button applies theme styles', () => {
      render(
        <ThemeProvider theme={theme}>
          <ThemedButton>Theme Test</ThemedButton>
        </ThemeProvider>
      );
    
      const button = screen.getByText('Theme Test');
      expect(button).toHaveStyleRule('background', '#0052cc');
      expect(button).toHaveStyleRule('padding', '16px');
    });
    

    To simplify testing with themes, define a customRender function:

    const customRender = (ui, theme = defaultTheme) => {
      return render(
        <ThemeProvider theme={theme}>
          {ui}
        </ThemeProvider>
      );
    };
    

    This approach keeps your tests clean and reusable. After setting up, move on to snapshot testing to validate component outputs.

    Snapshot Test Guide

    Snapshot tests save a serialized version of the component’s output to compare against future changes:

    test('styled component matches snapshot across prop variations', () => {
      const { container, rerender } = render(
        <ThemeProvider theme={theme}>
          <StyledCard variant="primary" elevated>
            <h2>Card Title</h2>
          </StyledCard>
        </ThemeProvider>
      );
    
      expect(container.firstChild).toMatchSnapshot();
    
      rerender(
        <ThemeProvider theme={theme}>
          <StyledCard variant="secondary" elevated={false}>
            <h2>Card Title</h2>
          </StyledCard>
        </ThemeProvider>
      );
    
      expect(container.firstChild).toMatchSnapshot();
    });
    

    Tips for effective snapshot testing:

    • Keep snapshots concise and focused on specific elements.
    • Carefully review changes in snapshot diffs to avoid missing unintended updates.
    • Use jest -u to update snapshots only when necessary.
    • Avoid using snapshots for components that frequently change, as this can lead to excessive updates.

    Once snapshots are in place, you can test how global styles interact with your components.

    Global Style Testing

    Global styles can be tested with the following approach:

    const GlobalStyle = createGlobalStyle`
      body {
        margin: 0;
        font-family: 'Arial', sans-serif;
      }
    
      * {
        box-sizing: border-box;
      }
    `;
    
    test('component renders correctly with global styles', () => {
      const { container } = render(
        <>
          <GlobalStyle />
          <StyledComponent />
        </>
      );
    
      const styles = window.getComputedStyle(container.firstChild);
      expect(styles.boxSizing).toBe('border-box');
      expect(styles.fontFamily).toMatch(/Arial/);
    });
    

    For components that modify global styles, ensure test isolation by cleaning up styles after each test:

    afterEach(() => {
      document.head.querySelector('style').remove();
      jest.clearAllMocks();
    });
    

    This ensures that your test environment remains consistent and unaffected by previous tests.

    Testing Tips and Common Errors

    Writing Clear Tests

    Focus on testing key behaviors and styles rather than every single CSS detail:

    // Avoid this approach
    test('button has correct styles', () => {
      const { getByRole } = render(<StyledButton>Click me</StyledButton>);
      const button = getByRole('button');
    
      expect(button).toHaveStyle({
        backgroundColor: '#0052cc',
        padding: '8px 16px',
        borderRadius: '4px',
        fontSize: '14px',
        fontWeight: '500',
        lineHeight: '1.5',
        // Testing every single style property is unnecessary
      });
    });
    
    // A better approach - focus on critical styles
    test('button renders with primary styling', () => {
      const { getByRole } = render(<StyledButton variant="primary">Click me</StyledButton>);
      const button = getByRole('button');
    
      expect(button).toHaveStyle({
        backgroundColor: '#0052cc',
        padding: '8px 16px'
      });
    });
    

    Keep tests concise and organized. Use describe blocks with clear, descriptive names to group related tests:

    describe('StyledButton', () => {
      describe('variant styles', () => {
        test('applies primary variant styles correctly', () => {
          // Test primary variant
        });
    
        test('applies secondary variant styles correctly', () => {
          // Test secondary variant
        });
      });
    });
    

    Snapshot Testing Limits

    While snapshots can be helpful, it’s important to use them wisely:

    When to Use Snapshots When to Avoid Snapshots
    Static components with minimal props Components with frequent style updates
    UI elements that rarely change Dynamic content rendering
    Basic layout verification Complex interactive components
    Documenting component structure Components with many prop combinations

    For components with dynamic styles, it’s better to use explicit style assertions instead of relying on snapshots:

    test('dynamic styles update correctly', () => {
      const { rerender, getByRole } = render(
        <StyledButton size="small">Click me</StyledButton>
      );
    
      let button = getByRole('button');
      expect(button).toHaveStyle({ padding: '4px 8px' });
    
      rerender(<StyledButton size="large">Click me</StyledButton>);
      expect(button).toHaveStyle({ padding: '12px 24px' });
    });
    

    Combining these strategies with design system tools can make your testing more consistent and efficient.

    UXPin Integration

    UXPin

    Using tools like UXPin can further improve your testing process by aligning your development work with design systems. For example, UXPin’s React libraries allow you to apply the same testing patterns to components:

    test('UXPin component renders with design system tokens', () => {
      const { getByRole } = render(
        <ThemeProvider theme={uxpinTheme}>
          <DesignSystemButton variant="primary">
            Design System Button
          </DesignSystemButton>
        </ThemeProvider>
      );
    
      const button = getByRole('button');
      expect(button).toHaveStyle({
        backgroundColor: uxpinTheme.colors.primary,
        borderRadius: uxpinTheme.radii.medium
      });
    });
    

    UXPin’s Merge technology ensures consistency by keeping your tested components and design prototypes in sync, reducing potential mismatches between design and development.

    Summary

    Using React Testing Library to test styled components helps ensure your app is reliable, easier to maintain, and consistently designed.

    Key Advantages

    • Spot styling issues early in development.
    • Minimize unexpected changes in the user interface.
    • Test style props, dynamic styles, and theme variations to maintain a cohesive look and feel.

    Connection to Design Systems

    Thorough component testing plays a crucial role in supporting design system workflows.

    Why Testing Matters

    Testing styled components boosts code quality, simplifies ongoing maintenance, and improves team collaboration. By adopting regular testing routines, teams can deliver high-quality React apps while keeping the development process efficient.

    "As a full stack design team, UXPin Merge is our primary tool when designing user experiences. We have fully integrated our custom-built React Design System and can design with our coded components. It has increased our productivity, quality, and consistency, streamlining our testing of layouts and the developer handoff process."

    Related Blog Posts

    Still hungry for the design?

    UXPin is a product design platform used by the best designers on the planet. Let your team easily design, collaborate, and present from low-fidelity wireframes to fully-interactive prototypes.

    Start your free trial

    These e-Books might interest you