No one taught us testing the right way.
They said, “Write tests” but never showed us how.
We'll start from very basics and go all the way to advanced logic and React component testing.
No theory. Just clear examples and explanations.
Unit testing means testing one small piece of logic — like a function or a component — in isolation.
You don’t test everything at once — just one unit.
We’ll use the Jest framework for writing unit tests.
To set it up in your project, follow the Jest getting started guide.
Let’s say you have a function to add two numbers:
// math.ts
export const add = (a: number, b: number) => a + b;
If I tell you to write a unit test for this —
How would you approach it?
What’s coming in your mind?
2 + 3
→ result should be 5
Exactly. You’re thinking right.
But how do you write the test for this?
// math.test.ts
import { add } from './math';
test('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
add()
from math.ts
2
and 3
5
That’s it.
Congrats — you just wrote your first unit test.
Let’s build a divide()
function and test its logic + error case.
// divide.ts
export const divide = (a: number, b: number) => {
if (b === 0) throw new Error('Cannot divide by zero');
return a / b;
};
Exactly. Now how do you write the test for this?
// divide.test.ts
import { divide } from './divide';
test('divides two numbers', () => {
expect(divide(10, 2)).toBe(5);
});
test('throws error on divide by zero', () => {
expect(() => divide(10, 0)).toThrow('Cannot divide by zero');
});
That’s how you test both logic and error handling.
You built a form. You want to test if error shows when input is wrong.
// EmailForm.tsx
import { useState } from 'react';
export function EmailForm() {
const [email, setEmail] = useState('');
const [msg, setMsg] = useState('');
const handle = () => {
if (!email.includes('@')) {
setMsg('Invalid email');
} else {
setMsg('Submitted');
}
};
return (
<>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
<button onClick={handle}>Submit</button>
<p>{msg}</p>
</>
);
}
@
Submitted
Invalid email
Exactly. Now how do you test it?
// EmailForm.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { EmailForm } from './EmailForm';
test('shows error on invalid email', () => {
render(<EmailForm />);
fireEvent.change(screen.getByPlaceholderText('Enter email'), {
target: { value: 'gaurav' },
});
fireEvent.click(screen.getByText('Submit'));
expect(screen.getByText('Invalid email')).toBeInTheDocument();
});
gaurav
)Invalid email
You just tested user interaction + validation — all in one test.
Here’s an async function that calls an API and returns user data:
// getUser.ts
export const getUser = async (id: string) => {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error('User not found');
return res.json();
};
id
res.ok
is false → throws errorJSON
But here's the thing:
You don’t call real APIs in unit tests
You mock them usingjest.fn()
// getUser.test.ts
import { getUser } from './getUser';
global.fetch = jest.fn();
beforeEach(() => {
(fetch as jest.Mock).mockReset();
});
test('returns user data when API succeeds', async () => {
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
json: async () => ({ id: '123', name: 'Gaurav' }),
});
const result = await getUser('123');
expect(result.name).toBe('Gaurav');
});
test('throws error when API fails', async () => {
(fetch as jest.Mock).mockResolvedValueOnce({ ok: false });
await expect(getUser('123')).rejects.toThrow('User not found');
});
fetch()
with a mockThis is how you test async logic with clean mocks.
Start small.
Test one thing today.
Don’t aim for 100% test coverage.
Aim for 100% confidence.
If a bug could have been caught with a 5-line test — write the test.
You’ll thank yourself later.
I'll cover:
Let me know — I’ll write it.
Until then — write tests like a builder, not a robot.
I share raw, no-fluff posts on building products, writing better code, and growing as a developer.