Labs ICT
Pro Login

Unit Testing

Testing individual components.

Unit Testing

Unit testing is testing one piece in isolation. Like checking individual ingredients before cooking a meal. Each function, each component gets tested on its own to make sure it works correctly.

Unit tests are fast, focused, and easy to debug. When something fails, you know exactly where the problem is.

Testing Components

The simplest test renders a component and checks that it displays the right content. This catches typos, missing props, and basic rendering issues.

import { render, screen } from '@testing-library/react'
import UserCard from './UserCard'

test('renders user name and email', () => {
  render(<UserCard name="Alice" email="alice@test.com" />)

  expect(screen.getByText('Alice')).toBeInTheDocument()
  expect(screen.getByText('alice@test.com')).toBeInTheDocument()
})

test('shows avatar image', () => {
  render(<UserCard name="Alice" avatar="/alice.jpg" />)
  const img = screen.getByRole('img')
  expect(img).toHaveAttribute('src', '/alice.jpg')
})

getByText finds elements by their visible text. getByRole finds elements by their accessibility role. Both approaches test what users actually see.

Testing Props and State

Props determine what a component receives. State determines what it holds internally. Testing both ensures your component reacts correctly to different scenarios.

import { render, screen } from '@testing-library/react'
import Counter from './Counter'

test('starts at zero', () => {
  render(<Counter />)
  expect(screen.getByText('Count: 0')).toBeInTheDocument()
})

test('increments on click', async () => {
  const user = userEvent.setup()
  render(<Counter />)

  await user.click(screen.getByText('+'))
  expect(screen.getByText('Count: 1')).toBeInTheDocument()

  await user.click(screen.getByText('+'))
  expect(screen.getByText('Count: 2')).toBeInTheDocument()
})

test('accepts initial count prop', () => {
  render(<Counter initialCount={5} />)
  expect(screen.getByText('Count: 5')).toBeInTheDocument()
})

Notice how we test behavior, not state directly. Click the button, check the text. This is how users interact with your component.

Mocking

Mocking replaces real dependencies with fake versions. Need to call an API in a test? Mock it. Need a timer? Mock it. This keeps tests fast and predictable.

import { render, screen, waitFor } from '@testing-library/react'
import UserProfile from './UserProfile'

global.fetch = jest.fn()

test('loads and displays user data', async () => {
  fetch.mockResolvedValueOnce({
    ok: true,
    json: async () => ({ name: 'Alice', role: 'Admin' })
  })

  render(<UserProfile id={1} />)

  expect(screen.getByText('Loading...')).toBeInTheDocument()

  await waitFor(() => {
    expect(screen.getByText('Alice')).toBeInTheDocument()
  })
})

test('shows error when fetch fails', async () => {
  fetch.mockResolvedValueOnce({ ok: false })

  render(<UserProfile id={1} />)

  await waitFor(() => {
    expect(screen.getByText(/something went wrong/i)).toBeInTheDocument()
  })
})

The mock fetch returns controlled data. Tests run instantly without network calls. Plus you can test error scenarios that would be hard to reproduce with real APIs.

Try it Yourself →