Testing React Components with Jest: A Simple Guide 🚀
Why Test Your Code? 🤔
Before we jump into the technical stuff, let’s answer the big question:
Why test your code? Testing ensures your app doesn’t crash or behave unexpectedly. It helps catch bugs early and keeps your users happy. 😊
Imagine building a house without checking if the walls are stable. 🏠 Testing is like making sure every brick in your app is solid!
Meet Our Component: Email Signup ✉️
Here’s the deal: we have a React component called EmailSignup. It lets users enter their email and, when they hit “Submit”, the app does its magic. ✨ But how do we make sure this magic works? We test it!
What Are We Testing? 📝
For our EmailSignup component, we’ll test:
- The initial state: Does it show the correct text when the page loads?
- If the email from the URL shows up in the input field.
- If it properly handles a loading state (when the form is submitting).
- If it shows an error message when something goes wrong.
- If it sends the right information when the user submits the form
Tools We’ll Use 🛠️
- Jest: A testing framework that lets us write and run tests.
- React Testing Library: A set of helpers that lets us interact with React components like a user would.
We’ll also mock some hooks like useRouter
(for simulating URL queries) and useDispatch
(for simulating Redux actions).
The Test Code 🖥️
Let’s take a look at our test cases! 🚀 Don’t worry if you’re not a coding expert — I’ll explain each step along the way.
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import EmailSignup from './EmailSignup';
import { useRouter } from 'next/router';
import { useDispatch } from 'react-redux';
import useDevice from './useDevice';
// Mocking hooks
jest.mock('next/router', () => ({
useRouter: jest.fn(),
}));
jest.mock('react-redux', () => ({
useDispatch: jest.fn(),
}));
jest.mock('./useDevice', () => jest.fn());
describe('EmailSignup Component', () => {
let mockRouter, mockDispatch, mockDevice;
beforeEach(() => {
mockRouter = { query: {} }; // Fake router data
mockDispatch = jest.fn(); // Fake dispatch function
mockDevice = 'desktop'; // Fake device context
useRouter.mockReturnValue(mockRouter);
useDispatch.mockReturnValue(mockDispatch);
useDevice.mockReturnValue(mockDevice);
});
test('renders with initial state', () => {
render(<EmailSignup />); // Render the component
expect(screen.getByPlaceholderText(/enter your email/i)).toBeInTheDocument();
expect(screen.getByText(/Please check your email/i)).toBeInTheDocument();
});
test('uses email from router query if available', () => {
mockRouter.query = { email: 'test@example.com' };
render(<EmailSignup />);
const emailInput = screen.getByPlaceholderText(/enter your email/i);
expect(emailInput.value).toBe('test@example.com');
});
test('handles loading state', () => {
render(<EmailSignup />);
const submitButton = screen.getByRole('button', { name: /submit/i });
fireEvent.click(submitButton); // Simulate a click on the submit button
expect(screen.getByRole('button')).toBeDisabled(); // Check if the button is disabled
});
test('dispatches action on successful submit', () => {
mockRouter.query = { email: 'test@example.com' };
render(<EmailSignup />);
fireEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(mockDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called
expect(mockDispatch).toHaveBeenCalledWith({
type: 'SIGNUP',
payload: { email: 'test@example.com' },
});
});
});
Breaking Down the Tests 🛠️
Let’s break down what’s happening:
- Mocking: We use Jest’s
jest.mock()
to fakeuseRouter
,useDispatch
, anduseDevice
. This lets us control what data these hooks return so we can test different scenarios. - Rendering the Component: We use
render(<EmailSignup />)
to render the component, so we can interact with it - Testing Initial State: The first test checks if the component loads correctly with a default email input and a message prompting the user to check their email.
- Testing URL Query: If an email is passed via the URL (mocked with
useRouter
), the second test ensures it shows up in the input field. - Loading State: When the form is submitted, the loading state should disable the submit button. We simulate this using
fireEvent.click()
. - Dispatching Actions: Finally, we test if the correct action is dispatched with the right email when the form is submitted.
What Did We Achieve? 🎯
By writing these simple tests, we made sure:
- Our form behaves as expected when the page loads.
- The email from the URL appears in the form field.
- The loading state and error messages work properly.
- The form sends the right data when submitted.
In short: Our tests make sure our component works as expected, and any bugs 🐛 caught now are bugs not seen by your users. 🎉