Testing Basics

Back

Loading concept...

🧪 Testing React: Your Safety Net for Fearless Coding


The Story of the Worried Chef

Imagine you’re a chef in a busy restaurant. You’ve created a brand new recipe—a chocolate cake that everyone loves. But what if one day you accidentally add salt instead of sugar? 😱

That’s where testing comes in!

Before serving your cake to real customers, you have a trusted friend who tastes it first. They check:

  • Is it sweet? ✅
  • Is it moist? ✅
  • Does it look good? ✅

Only after passing these “tests” do you serve it to customers.

React testing is exactly like having this trusted friend for your code!


🎯 Testing Philosophy: Why We Test

The Big Idea

Testing isn’t about proving your code is perfect. It’s about catching mistakes before your users do.

Think of it like this:

You build a toy car 🚗
Before giving it to your friend:
  - Do the wheels spin? ✅
  - Does the door open? ✅
  - Does it roll straight? ✅

The Golden Rule

“Write tests that check what users see and do, not how the code works inside.”

Bad test: “Check if the state variable changed to trueGood test: “Check if the button says ‘Success’ after clicking”

Why This Matters

graph TD A["You write code"] --> B["Tests run automatically"] B --> C{All tests pass?} C -->|Yes| D["Safe to share! 🎉"] C -->|No| E["Fix the problem first"] E --> A

🛠️ React Testing Library: Your Testing Toolkit

What Is It?

React Testing Library (RTL) is like a robot that pretends to be a user. It:

  • Looks at your app like a person would
  • Clicks buttons like a person would
  • Reads text like a person would

Setting It Up

// Install in your project
npm install --save-dev
  @testing-library/react
  @testing-library/jest-dom

Your First Test File

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

test('shows button text', () => {
  render(<Button>Click Me</Button>);

  const button = screen.getByText('Click Me');
  expect(button).toBeInTheDocument();
});

What’s happening here?

  1. We bring in our tools (render, screen)
  2. We show the Button on a pretend screen
  3. We look for text “Click Me”
  4. We check: “Is it really there?”

🎬 Component Rendering: Showing Your App on the Test Stage

The Concept

“Rendering” means putting your component on a test screen so we can check it.

Think of it like a dress rehearsal before the real show:

Real Stage = Your user's browser
Test Stage = Testing Library's pretend screen

How to Render

import { render } from
  '@testing-library/react';
import Greeting from './Greeting';

test('shows hello message', () => {
  // Put component on test stage
  render(<Greeting name="Sam" />);

  // Now we can check what's showing!
});

Rendering with Props

// Testing different scenarios
test('greets the user by name', () => {
  render(<Greeting name="Alex" />);

  expect(screen.getByText('Hello, Alex!'))
    .toBeInTheDocument();
});

test('shows default when no name', () => {
  render(<Greeting />);

  expect(screen.getByText('Hello, Friend!'))
    .toBeInTheDocument();
});

🔍 Element Queries: Finding Things on the Screen

The Big Picture

After rendering, you need to FIND elements to check them. It’s like playing hide and seek with your components!

The Query Family

graph TD A["How to find elements?"] --> B["getBy..."] A --> C["queryBy..."] A --> D["findBy..."] B --> E["Throws error if not found"] C --> F["Returns null if not found"] D --> G["Waits for element - async"]

The Most Important Queries

By Role - Best choice! Finds by accessibility role:

// Find a button
screen.getByRole('button', { name: 'Submit' });

// Find a heading
screen.getByRole('heading', { name: 'Welcome' });

// Find a textbox
screen.getByRole('textbox', { name: 'Email' });

By Text - Finds exact text on screen:

screen.getByText('Hello World');
screen.getByText(/hello/i); // case-insensitive

By Label - Finds form inputs by their label:

screen.getByLabelText('Username');
screen.getByLabelText('Password');

By Placeholder - Finds by placeholder text:

screen.getByPlaceholderText('Enter email...');

By Test ID - Last resort option:

// In your component:
<div data-testid="custom-element">Hi</div>

// In your test:
screen.getByTestId('custom-element');

Which Query Should I Use?

Priority Query When to Use
1st getByRole Always try first!
2nd getByLabelText Form inputs
3rd getByText Non-interactive text
4th getByPlaceholderText Input fields
Last getByTestId When nothing else works

📸 Snapshot Testing: Taking Photos of Your Code

The Concept

Imagine taking a photo of your room. Next week, you take another photo. If something moved, you’d notice!

Snapshot testing works the same way:

Day 1: Take "photo" of your component
Day 7: Take new "photo"
Compare: Anything different? 🤔

How It Works

import { render } from
  '@testing-library/react';
import Card from './Card';

test('Card looks correct', () => {
  const { container } = render(
    <Card title="Hello" body="World" />
  );

  // Take a "photo" (snapshot)
  expect(container).toMatchSnapshot();
});

First Run

When you run this test the first time:

  • Jest creates a snapshot file
  • It saves what your component looks like

Future Runs

On future test runs:

  • Jest compares to the saved snapshot
  • If different: Test fails! ⚠️
  • You decide: Was this change intentional?

Updating Snapshots

# If change was intentional:
npm test -- -u

# Or press 'u' in watch mode

When to Use Snapshots

✅ Good for:

  • Checking overall structure
  • Catching accidental changes
  • Quick coverage for simple components

❌ Not good for:

  • Testing behavior
  • Dynamic content
  • Large, complex components

♿ Accessibility Testing: Making Apps for Everyone

Why It Matters

Imagine if a friend couldn’t use your app because:

  • They can’t see well
  • They can’t use a mouse
  • They use a screen reader

Good developers build for EVERYONE!

Built-In Accessibility with RTL

The best part? Using getByRole queries automatically encourages accessible code!

// This test only passes if your
// component is accessible:
screen.getByRole('button', { name: 'Save' });

// For this to work, your button needs:
// - Proper button element
// - Accessible name (text or aria-label)

Testing Accessible Names

test('buttons have accessible names', () => {
  render(<Form />);

  // These will fail if not accessible!
  expect(
    screen.getByRole('button', { name: 'Submit' })
  ).toBeInTheDocument();

  expect(
    screen.getByRole('button', { name: 'Cancel' })
  ).toBeInTheDocument();
});

Using jest-axe for Deep Checks

import { axe, toHaveNoViolations } from
  'jest-axe';

expect.extend(toHaveNoViolations);

test('component has no a11y violations',
  async () => {
  const { container } = render(<MyForm />);
  const results = await axe(container);

  expect(results).toHaveNoViolations();
});

Common Accessibility Checks

graph TD A["Accessibility Checks"] --> B["Images have alt text"] A --> C["Forms have labels"] A --> D["Buttons have names"] A --> E["Headings are in order"] A --> F["Color contrast is good"]

🎯 Putting It All Together

Here’s a complete example combining everything:

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

describe('LoginForm', () => {

  test('renders all form elements', () => {
    render(<LoginForm />);

    // Find by accessible roles
    expect(screen.getByRole('textbox',
      { name: 'Email' }
    )).toBeInTheDocument();

    expect(screen.getByLabelText('Password'))
      .toBeInTheDocument();

    expect(screen.getByRole('button',
      { name: 'Log In' }
    )).toBeInTheDocument();
  });

  test('shows error for empty submit',
    async () => {
    render(<LoginForm />);
    const user = userEvent.setup();

    await user.click(
      screen.getByRole('button',
        { name: 'Log In' })
    );

    expect(screen.getByText(
      'Email is required'
    )).toBeInTheDocument();
  });

});

🌟 Key Takeaways

  1. Testing Philosophy: Test what users see, not how code works internally

  2. React Testing Library: A toolkit that tests like a real user would

  3. Component Rendering: render() puts your component on a test stage

  4. Element Queries: Use getByRole first, getByTestId last

  5. Snapshot Testing: Takes “photos” to catch unexpected changes

  6. Accessibility Testing: Build apps everyone can use!


🚀 You’re Ready!

You now understand the foundations of React testing. Like our chef friend with the trusted taste-tester, you have the tools to catch problems before they reach your users.

Remember: Tests are your safety net. The more you practice, the more confident you become!

Happy Testing! 🧪✨

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.