Gaurav Nardia

Jul 28, 2025 • 4 min read

How to Write Unit Tests in JavaScript/Typescript Using Jest

Learn unit testing from scratch with real examples, clear explanations, and practical code you can use today.

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.

What is Unit Testing?

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 will gonna use the Jest framerwork for writing unit test for set it up in project you can read the official docs here: https://jestjs.io/docs/getting-started

Let's start it with simple example,

Let's say you've a simple example of adding to numbers

// math.ts
export const add = (a: number, b: number) => a + b;

If i tell you write unit test for this function

How would you approach this ?

What's coming in your mind ?

Now will say that in input only numbers are allowed and it return only numbers also.

Exactly! you're thinking right, but how do you write test for this ?

// math.test.ts
import { add } from './math';

test('adds two numbers', () => {
  expect(add(2, 3)).toBe(5);
});

What happened here?

  • We imported add() from math.ts file

  • We gave it 2 and 3

  • We checked if the result was 5

That's all you've to do.

Congrats — you wrote your first unit test.

Now let's take another example.

Make a divide function and test its error

// divide.ts
export const divide = (a: number, b: number) => {
  if (b === 0) throw new Error('Cannot divide by zero');
  return a / b;
};

In this what's coming in your mind ?

  1. only numbers can be divided.

  2. We can't divide a number from 0.

  3. Return must be number also.

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');
});

In this 1st test we wrote the test for 1 & 3 condition that numbers can be divided and returned value must be number.

In 2nd test, wrote the test for the error that can come (2nd condition) that Number cant divided by 0.

That's how you write the test for this.

Let's take 2 more advance examples so that you can master the unit testing and all your doubts become clear.

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>
    </>
  );
}

In this what's coming in your mind ?

  1. Input must be a string

  2. It must contain @

  3. It must end with .com

  4. If all good → return true

  5. If not → throw error

Exactly, now how do you write the test for this ?

// 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();
});

In this test, what’s coming to your mind?

  1. You entered invalid email: gaurav (no @)

  2. You clicked Submit

  3. The UI should show Invalid email

  4. You’re checking if that exact text is in the DOM

This is a unit test that covers:

  • User typing

  • Validation logic

  • Final message showing correctly

That’s how you test form behavior without running a full app

And the last example is

Async API Call with Mock

// 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();
};

Now, what’s the logic here?

  1. It takes a user id

  2. Calls an API endpoint

  3. If status is not ok → throws error

  4. If success → returns JSON data

But here's the thing:
You don’t hit the real API. that's where mocking comes. we create a response just like coming from API so that we can prevent api for unnecessary hitting.

// 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');
});

What’s happening in your mind here?

  1. We're not calling real API — we replaced fetch() with a fake (mock).

  2. First test returns success JSON with user

  3. Second test simulates a failed API and expects error

  4. All tests are isolated, fast, and don’t depend on network

This is how you test async logic with clean mocks.

And that's how you write unit tests.

Start small.
Test one thing today.
Don’t aim for 100% 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.

Join Gaurav on Peerlist!

Join amazing folks like Gaurav and thousands of other builders on Peerlist.

peerlist.io/

It’s available... this username is available! 😃

Claim your username before it's too late!

This username is already taken, you’re a little late.😐

0

8

0