Skip to main content

Unit and Integration Testing

Unit testing checks one isolated part, while integration testing checks multiple parts working together.

One function or component  -> Unit Test
Connected modules or UI -> Integration Test

One-line idea: Unit tests prove that the pieces work; integration tests prove that the pieces work together.

Core Concepts

What Is Unit Testing?

Unit testing verifies a small, independent unit of code in isolation.

A unit can be:

  • a JavaScript function
  • a React component
  • a utility
  • a reducer
  • a validation rule

Example questions:

Does this sorting function return the correct order?
Does this button render the correct label?
Does this validator reject an empty email?

Unit tests are usually:

  • small
  • fast
  • predictable
  • independent from external systems

What Is Integration Testing?

Integration testing verifies that multiple units interact correctly.

It can check interactions between:

  • parent and child components
  • form fields and validation logic
  • a component and an API layer
  • multiple modules
  • UI actions and state updates

Example questions:

Does clicking Add to Cart update the cart count?
Does submitting the form display validation errors?
Does loaded API data appear in the component?

Unit vs Integration Testing

AreaUnit TestingIntegration Testing
ScopeOne isolated unitMultiple connected units
GoalVerify individual behaviorVerify interaction between parts
DependenciesUsually avoided or replacedSome real dependencies work together
SpeedUsually very fastUsually slower than a unit test
Failure meaningA specific unit is incorrectThe connection or combined behavior is incorrect
ExampleTest sortUsersByAge()Test button → state update → UI change
Unit test
[ Function ]

Integration test
[ Component ] <-> [ State ] <-> [ Child Component ]

What Is Component Testing?

Component testing verifies a UI component's rendering and interaction.

It may be:

  • a unit test when one small component is tested in isolation
  • an integration test when the component interacts with children, state, context, routing or another module

The label depends on the test scope, not only on the tool being used.

Testing Tools and Their Roles

ToolRole
JestRuns tests and provides assertions
JSDOMSimulates a browser-like DOM inside Node.js
React Testing LibraryTests rendered UI through user-visible behavior
VitestFast alternative test runner
CypressCommonly used for integration and E2E browser tests
PlaywrightAutomates real browsers for application flows

Jest can be used for both unit and integration tests.

JSDOM allows DOM-based tests to run without opening a real browser.

React Testing Library encourages testing what users can see and do instead of testing internal implementation details.

Practical Testing Examples

Basic Jest Setup

Create a project and install Jest:

npm init -y
npm install --save-dev jest

Add a test script to package.json:

package.json
{
"scripts": {
"test": "jest"
}
}

Run the test suite:

npm test

Test File Naming

Common naming conventions are:

app.test.js
app.spec.js

A test file is usually placed near the source file or inside a dedicated test directory.

src/
├── app.js
└── app.test.js

Unit Test: Sorting Users by Age

Create a pure function that receives users and returns them in descending age order.

app.js
function sortUsersByAge(users) {
return [...users].sort((a, b) => b.age - a.age);
}

module.exports = sortUsersByAge;

[...users] creates a copy so the original array is not changed by sort().

Now test the function:

app.test.js
const sortUsersByAge = require("./app");

const users = [
{ name: "Akshay", age: 28 },
{ name: "Simran", age: 30 },
{ name: "Sachin", age: 50 },
{ name: "Aman", age: 20 },
];

test("places the oldest user first", () => {
const sortedUsers = sortUsersByAge(users);

expect(sortedUsers[0].name).toBe("Sachin");
});

test("places the youngest user last", () => {
const sortedUsers = sortUsersByAge(users);

expect(sortedUsers[sortedUsers.length - 1].name).toBe("Aman");
});

test("keeps all users in the result", () => {
const sortedUsers = sortUsersByAge(users);

expect(sortedUsers).toHaveLength(4);
});

Understand the Jest Test Structure

test("description of expected behavior", () => {
const result = functionUnderTest();

expect(result).toBe(expectedValue);
});
Arrange -> prepare input
Act -> run the unit
Assert -> check the result

Applied to the sorting test:

Arrange -> create the users array
Act -> call sortUsersByAge(users)
Assert -> verify the first, last and total items

Common Jest Matchers

MatcherUseExample
toBe()Primitive or exact valueexpect(count).toBe(2)
toEqual()Object or array structureexpect(user).toEqual(expectedUser)
toBeTruthy()Truthy resultexpect(isValid).toBeTruthy()
toBeFalsy()Falsy resultexpect(hasError).toBeFalsy()
toContain()Item inside an array or textexpect(names).toContain("Aman")
toHaveLength()Array or string lengthexpect(users).toHaveLength(4)

Unit Test: A Small React Component

The component receives a value and renders it.

Greeting.jsx
export default function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}
Greeting.test.jsx
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import Greeting from "./Greeting";

test("renders the supplied name", () => {
render(<Greeting name="Aman" />);

expect(
screen.getByRole("heading", { name: "Hello, Aman" }),
).toBeInTheDocument();
});

This remains a focused unit-style component test because it checks one component with simple props.

Integration Test: Cart Components Working Together

Suppose one component adds a product and another displays the current count.

CartPage.jsx
import { useState } from "react";

function CartCount({ count }) {
return <p>Cart items: {count}</p>;
}

function AddToCartButton({ onAdd }) {
return <button onClick={onAdd}>Add to Cart</button>;
}

export default function CartPage() {
const [count, setCount] = useState(0);

return (
<main>
<CartCount count={count} />
<AddToCartButton onAdd={() => setCount((value) => value + 1)} />
</main>
);
}

Test the complete interaction:

CartPage.test.jsx
import { fireEvent, render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import CartPage from "./CartPage";

test("updates the cart count after adding an item", () => {
render(<CartPage />);

expect(screen.getByText("Cart items: 0")).toBeInTheDocument();

fireEvent.click(screen.getByRole("button", { name: "Add to Cart" }));

expect(screen.getByText("Cart items: 1")).toBeInTheDocument();
});

This is an integration test because it verifies the connection between:

Button click

Parent state update

New count passed as a prop

CartCount renders the updated value

Test User-Visible Behavior

Prefer assertions based on behavior visible to the user.

Prefer:

screen.getByRole("button", { name: "Add to Cart" });
screen.getByText("Cart items: 1");

Avoid depending heavily on:

internal state variables
private helper calls
component implementation details

Tests based on visible behavior are more likely to remain valid when internal code is refactored.

When to Use Each Type

Use unit tests for:

  • pure functions
  • calculation logic
  • validation rules
  • reducers
  • small focused components

Use integration tests for:

  • parent-child behavior
  • form submission and validation
  • component-state interaction
  • modules sharing data
  • UI and API interaction

Use both when a feature contains important individual logic and important connections between parts.

Benefits, Trade-Offs and Common Mistakes

AreaUnit TestsIntegration Tests
Main strengthFast and precise feedbackHigher confidence in real feature behavior
Main limitationMay miss connection problemsFailures can be harder to locate
MaintenanceUsually small and focusedCan require more setup and test data

Common mistakes

MistakeBetter approach
Testing several modules but calling it a unit testName the test by its real scope
Depending on an external system in every unit testKeep unit tests isolated
Testing React internalsTest rendered output and user interactions
Writing only unit testsAdd integration tests for important connections
Writing only large integration testsUnit-test important isolated logic too
Using vague test namesDescribe the expected behavior clearly
Mutating shared test dataCreate fresh data or copy before mutation

Interview Revision

Quick Revision Checklist

  • Unit testing checks one function or component in isolation.
  • Integration testing checks multiple units working together.
  • Unit tests are usually faster and more focused.
  • Integration tests provide confidence in connected behavior.
  • Jest runs tests and provides assertions.
  • JSDOM simulates a DOM environment inside Node.js.
  • React Testing Library focuses on visible behavior and interactions.
  • Test files commonly use .test.js or .spec.js.
  • test() defines a test case.
  • expect() creates an assertion.
  • Arrange, Act and Assert is a useful test structure.
  • A component test may be unit or integration depending on its scope.
  • Use unit and integration tests together for balanced confidence.

Frequently Asked Interview Questions

1. What is unit testing?

Unit testing verifies one small function, component or module in isolation.

2. What is integration testing?

Integration testing verifies that multiple units communicate and behave correctly together.

3. What is the main difference between unit and integration testing?

The difference is scope. Unit tests check one part; integration tests check connections between parts.

4. Can React components have unit tests?

Yes. A small component tested with simple props and no significant external interaction can be treated as a unit.

5. What is JSDOM?

JSDOM provides a browser-like DOM environment in Node.js so UI tests can render and query elements without opening a real browser.

6. Why use React Testing Library?

It encourages testing the UI through rendered content, accessible queries and user interactions rather than component internals.

7. Can Jest run integration tests?

Yes. Jest is a test runner and assertion library; the test type depends on how many units the test exercises.

8. Why do we need both unit and integration tests?

Unit tests catch mistakes in isolated logic, while integration tests catch problems in the way parts communicate.

9. What are common Jest matchers?

Common matchers include toBe, toEqual, toBeTruthy, toBeFalsy, toContain and toHaveLength.

Memory Trick

Piece -> Connection

Unit = test one piece
Integration = test the connection between pieces

One-Line Summary

Unit tests verify individual pieces, while integration tests verify that connected pieces produce the correct feature behavior.

Final Mental Model

Isolate one function or component

Verify its input and output

Combine related parts

Perform a user-visible action

Verify the complete connected result