Skip to main content

Debugging and Testing React

Level: Beginner

ℹ️ What You'll Learn
  • 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
⚠️ Testing Mistakes
  1. Only testing happy path — Test error scenarios too
  2. Testing implementation details — Test behavior, not internals
  3. No async handling — Use waitFor for async code
  4. Mock everything — Only mock external dependencies
💡 Test-Driven Development

Write tests BEFORE code:

  1. Write failing test
  2. Write code to make it pass
  3. Refactor

This ensures code is testable from the start.

🤖Use AI to Learn Faster

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:

  1. Build a small component or page for students, attendance, marks, or fees.
  2. Pass realistic data into the component.
  3. Add one success state and one empty/error state where relevant.
  4. Explain the data flow in your own words.
  5. Rebuild the same example once without looking at the article.

Quick Revision

QuestionAnswer
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.
🎯 How would you explain Debugging and Testing React in an interview?

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.

🎯 Where is this used in a real React project?

It is used in screens like student lists, admission forms, attendance dashboards, marks reports, routing pages, and API-connected admin panels.

Next Article

Deployment and Performance Optimization ->

nexcoding.in