Creating mocks for testing react code

Creating mocks for testing react code

When dealing with components that fetch data from APIs, writing tests can become really hard due to external dependencies. This is where mocks and stubs come in, providing a controlled environment to isolate and test your component’s logic. In this post we will explore what mocks and stubs are, how they differ, and how Mock Service Worker (MSW) simplifies API mocking in Next.js project using Jest and React Testing Library.

Mocks vs Stubs: What’s the Difference?

In testing, both mocks and stubs are types of test doubles – objects that simulate the behavior of real objects. They are used to isolate the code under test from the rest of the system.

Mock: Replicate the entire behavior of an object, including side effects and interactions with other components. Mocks are often used for complex dependencies with intricate behavior.

Stub: Provide a simplified implementation for a specific functionality. They typically focus on returning predefined values or performing specific actions without replicating the complete behavior. Stubs are ideal for isolating the interaction between your component and the mocked dependency.

Create Mocks with jest

For example, I have UbahJadwal component that depend on a custom useJadwalKonselor hooks for displaying some data.


What The hooks do is simply fetch data to be displayed inside a Modal. We can mock the implementation of the hooks to return the API response.

We can see the test is passed when we assert/expect values from the mock.

Mocking API calls

Let’s consider we want to mock an API call to fetch some data, what we can do is we mock the global fetch function like this.

global.fetch = jest.fn(
() =>
Promise.resolve({
json: () =>
Promise.resolve({ data: [{
nama_pasien: A,
is_pagi: true,
is_terjadwal: true,
skala_prioritas: 10,
created_at: 2024-01-01 00:00:00+0000,
id_pendaftaran: 00000000-0000-0000-0000-000000000000,
}]}),
ok: true,
}) as Promise<Response>
)

The above code will do the job. The problems come when we have multiple fetch functions inside one component (fetch with GET and POST/PUT methods).

Mocking with MSW

While mocks are effective, they require modifying the component’s internal logic or mocking global functions like fetch. This can become cumbersome for complex components with numerous API interactions. Here’s where MSW shines.

MSW is a library that lets you create a mock service worker that intercepts all network requests made from your frontend application. It allows you to define handlers that specify how to respond to specific API calls with predefined data or errors. This approach offers several benefits:

Isolation: Isolates your frontend from external dependencies for reliable and consistent tests.

Flexibility: Easily define various responses for different scenarios without modifying component logic.

Simplicity: Provides a cleaner testing experience compared to manual mocks and stubs.

Network Interception: Captures actual network requests for inspection and debugging.

Here’s a basic example of how to use MSW to mock a GET request:

import { rest } from msw
import { setupServer } from msw/node
const handlers = [
rest.get(
process.env.NEXT_PUBLIC_API_URL_1 +
/api/konseling/konselor/59f30d61-4cc6-4ec3-90c1-c11663217889/,
(req,res,ctx) => {
return res(ctx.json({
id: 59f30d61-4cc6-4ec3-90c1-c11663217889,
nama: Konselor B,
no_wa: 0857651920018,
jadwal_praktik: [
{
urutan_hari: 1,
nama_hari: Selasa,
waktu_mulai: 10:00:00,
waktu_selesai: 16:00:00
},
{
urutan_hari: 3,
nama_hari: Kamis,
waktu_mulai: 12:00:00,
waktu_selesai: 15:00:00
}
],
tipe: Konselor
}),ctx.status(200))
}
),
rest.get(process.env.NEXT_PUBLIC_API_URL_1 +
/api/konseling/konselor/2d142202-acbf-4cfd-8ba7-8e02d7857056/,
(req,res,ctx) => {
return res(ctx.json({
error: Konselor tidak ditemukan}
),ctx.status(404))}),

rest.get(process.env.NEXT_PUBLIC_API_URL_1 +
/api/konseling/konselor/39a3ee55-8bcb-4bc8-9212-875c92ff4311/,
(req,res,ctx) => {
return res(ctx.json({
INTERNAL_SERVER_ERROR: Internal Server Error}
),ctx.status(500))}),
]

const server = setupServer(…handlers)

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

describe(useDetailKonselor, () => {
it(should return detail konselor, async () => {
const { result } = renderHook(() => useDetailKonselor(59f30d61-4cc6-4ec3-90c1-c11663217889))

expect(result.current.isLoading).toBeTruthy()

await waitFor(() => expect(result.current.isLoading).toBeFalsy())
expect(result.current.data).toEqual({
id: 59f30d61-4cc6-4ec3-90c1-c11663217889,
nama: Konselor B,
no_wa: 0857651920018,
jadwal_praktik: [
{
urutan_hari: 1,
nama_hari: Selasa,
waktu_mulai: 10:00:00,
waktu_selesai: 16:00:00
},
{
urutan_hari: 3,
nama_hari: Kamis,
waktu_mulai: 12:00:00,
waktu_selesai: 15:00:00
}
],
tipe: Konselor
})
})

it(should return error when konselor not found, async () => {
const { result } = renderHook(() => useDetailKonselor(2d142202-acbf-4cfd-8ba7-8e02d7857056))
await waitFor(() => expect(result.current.error).toBeDefined())

expect(result.current.error?.message).toEqual(Konselor tidak ditemukan)
})
})

The Result :

Leave a Reply

Your email address will not be published. Required fields are marked *