Debugging and Testing React
Level: Beginner
- React DevTools for component inspection
- Browser console debugging
- Jest for unit testing
- React Testing Library for component tests
- Testing user interactions
- Mocking API calls
- Test coverage and strategies
- Testing SMS components: StudentCard, Form, etc.
Why This Matters
Debugging and Testing React is part of building maintainable React applications. You will use it when creating student dashboards, forms, tables, API-connected screens, routing flows, and reusable UI components.
The Problem
Beginners often write React code that works for a small demo but becomes difficult when data, forms, API calls, and reusable components grow. This lesson explains Debugging and Testing React in a way that helps you build predictable UI for real .NET Web API projects.
React DevTools
Install React DevTools browser extension.
Inspect Components
- Right-click → "Inspect" opens DevTools
- See component tree, props, state
- Edit state in real-time to test different scenarios
Track Re-renders
- DevTools > Profiler tab
- See which components re-render and why
- Identify performance issues
Common Debugging Techniques
1. Console Logging
function StudentDetail({ studentId }) {
const [student, setStudent] = useState(null);
useEffect(() => {
console.log('Effect running with studentId:', studentId);
fetch(`/api/students/${studentId}`)
.then(r => r.json())
.then(data => {
console.log('Fetched student:', data);
setStudent(data);
});
}, [studentId]);
console.log('Rendering StudentDetail with student:', student);
return <div>{student?.name}</div>;
}
2. Debugger Statement
function StudentForm() {
const [form, setForm] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
debugger; // Execution pauses here if DevTools open
console.log('Form:', form);
};
return <form onSubmit={handleSubmit}>{/* */}</form>;
}
3. Error Boundaries + Error Logging
class ErrorBoundary extends React.Component {
componentDidCatch(error, errorInfo) {
console.error('Error caught:', error, errorInfo);
// Send to logging service
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong</h1>;
}
return this.props.children;
}
}
Testing with Jest and Testing Library
Install
npm install --save-dev @testing-library/react jest
Basic Test
// StudentCard.test.js
import { render, screen } from '@testing-library/react';
import StudentCard from './StudentCard';
describe('StudentCard', () => {
test('renders student name', () => {
const student = { id: 1, name: 'Ravi Kumar', marks: 95 };
render(<StudentCard student={student} />);
expect(screen.getByText('Ravi Kumar')).toBeInTheDocument();
});
test('displays marks correctly', () => {
const student = { id: 1, name: 'Ravi', marks: 95 };
render(<StudentCard student={student} />);
expect(screen.getByText(/95/)).toBeInTheDocument();
});
});
Testing User Interactions
import { render, screen, fireEvent } from '@testing-library/react';
import StudentForm from './StudentForm';
describe('StudentForm', () => {
test('submits form with data', () => {
const handleSubmit = jest.fn();
render(<StudentForm onSubmit={handleSubmit} />);
const input = screen.getByPlaceholderText('Name');
fireEvent.change(input, { target: { value: 'Ravi' } });
const button = screen.getByText('Submit');
fireEvent.click(button);
expect(handleSubmit).toHaveBeenCalled();
});
test('shows validation error for empty name', async () => {
render(<StudentForm />);
const button = screen.getByText('Submit');
fireEvent.click(button);
expect(await screen.findByText('Name is required')).toBeInTheDocument();
});
});
Testing Async Code
import { render, screen, waitFor } from '@testing-library/react';
import StudentList from './StudentList';
describe('StudentList', () => {
test('loads and displays students', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
{ id: 1, name: 'Ravi' },
{ id: 2, name: 'Priya' }
])
})
);
render(<StudentList />);
// Wait for students to load
await waitFor(() => {
expect(screen.getByText('Ravi')).toBeInTheDocument();
});
expect(screen.getByText('Priya')).toBeInTheDocument();
});
});
Testing with Context
import { render, screen } from '@testing-library/react';
import StudentDashboard from './StudentDashboard';
import { AuthProvider } from './contexts/AuthContext';
describe('StudentDashboard', () => {
test('shows user name from context', () => {
render(
<AuthProvider initialUser={{ name: 'Admin' }}>
<StudentDashboard />
</AuthProvider>
);
expect(screen.getByText(/Admin/)).toBeInTheDocument();
});
});
Testing Patterns
Test Structure
describe('StudentCard', () => {
// Setup
beforeEach(() => {
// Run before each test
});
// Individual tests
test('description of what it should do', () => {
// Arrange
const student = { id: 1, name: 'Ravi' };
// Act
render(<StudentCard student={student} />);
// Assert
expect(screen.getByText('Ravi')).toBeInTheDocument();
});
// Cleanup
afterEach(() => {
// Run after each test
});
});
Test Different Scenarios
describe('StudentBadge', () => {
test.each([
['Active', 'badge-green'],
['Graduated', 'badge-blue'],
['Inactive', 'badge-red']
])('shows %s status with %s class', (status, className) => {
render(<StudentBadge status={status} />);
expect(screen.getByText(status)).toHaveClass(className);
});
});
Mock API Calls
// __mocks__/studentService.js
export const studentService = {
getAll: jest.fn(() =>
Promise.resolve([
{ id: 1, name: 'Ravi' },
{ id: 2, name: 'Priya' }
])
),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn()
};
// StudentList.test.js
jest.mock('./api/studentService');
import { render, screen, waitFor } from '@testing-library/react';
import { studentService } from './api/studentService';
import StudentList from './StudentList';
describe('StudentList', () => {
test('displays fetched students', async () => {
render(<StudentList />);
await waitFor(() => {
expect(screen.getByText('Ravi')).toBeInTheDocument();
});
expect(studentService.getAll).toHaveBeenCalled();
});
});
Coverage Report
npm test -- --coverage
Shows percentage of code covered by tests.
Goal: 70%+ coverage for critical paths.
Network Debugging
DevTools Network Tab
- See all fetch requests
- Check status, headers, response
- Throttle network speed to simulate slow connections
Example: API Debugging
// Add request logging
const fetchWithLogging = (url, options) => {
console.log('Request:', url, options);
return fetch(url, options)
.then(r => {
console.log('Response status:', r.status);
return r.json();
})
.then(data => {
console.log('Response data:', data);
return data;
})
.catch(err => {
console.error('Request failed:', err);
throw err;
});
};
Key Takeaways
- Use React DevTools for inspection
- Console logs for debugging
- Write tests for components
- Test user interactions
- Mock API calls
- Aim for 70%+ coverage
- Next: Deployment
- Only testing happy path — Test error scenarios too
- Testing implementation details — Test behavior, not internals
- No async handling — Use waitFor for async code
- Mock everything — Only mock external dependencies
Write tests BEFORE code:
- Write failing test
- Write code to make it pass
- Refactor
This ensures code is testable from the start.
Use ChatGPT, Claude, or Copilot to go deeper on Testing and Debugging. Try these prompts:
"What should you test in React components?""How do you test async code in Jest?""What's the difference between shallow and full render tests?""Quiz me on React testing"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
Quick Definitions
- Debugging and Testing React - The main React concept explained in this lesson.
- Component - A reusable function that returns UI.
- Props/state/effects - Core React ideas used to pass data, remember data, and run side effects.
- Real project usage - How this appears in forms, dashboards, routes, and API-connected pages.
Common Mistakes
- Copying React code without understanding data flow
- Mutating arrays or objects directly instead of creating new values
- Forgetting keys, dependencies, loading states, or error states where needed
- Putting too much logic in one component
- Not testing the screen with realistic School Management System data
Practice Task
Create a small React example using Debugging and Testing React. Keep it focused on one School Management System screen.
Suggested practice:
- Build a small component or page for students, attendance, marks, or fees.
- Pass realistic data into the component.
- Add one success state and one empty/error state where relevant.
- Explain the data flow in your own words.
- Rebuild the same example once without looking at the article.
Quick Revision
| Question | Answer |
|---|---|
| What is the main idea? | Understand and apply Debugging and Testing React in React. |
| Where is it used? | Student dashboards, forms, tables, routes, and API-connected screens. |
| What should beginners focus on? | Clear components, predictable data flow, and small examples. |
| What is the best debugging habit? | Check props, state, render output, and browser console step by step. |
Debugging and Testing React is a React concept used to build clear, reusable, and predictable user interfaces. I would explain the problem it solves, show a small component example, and mention a common mistake beginners should avoid.
It is used in screens like student lists, admission forms, attendance dashboards, marks reports, routing pages, and API-connected admin panels.