Back to Notes

Tutorial

Mastering API Testing with Mocking: A Step-by-Step Guide for Beginners

April 15, 2024

0min read

How to setup and use msw, covering different test cases for handling successful responses, errors, and edge cases.

Today, we're going to dive into the world of API testing with mocking, and I'll guide you through every step of the process. By the end of this post, you'll have a solid understanding of how to write robust tests without relying on external APIs, ensuring consistent and reliable results.

Before we begin, let's quickly go over some key concepts:

  • Mocking: Simulating the behavior of real objects or services within a controlled environment, allowing you to isolate and test specific parts of your codebase.

  • Mock Service Worker (MSW): A library that intercepts network requests and returns mocked responses, making it easier to test APIs without relying on actual servers.

Now, let's start by setting up the msw library. First, install it by running:

npm install msw --save-dev

Once installed, we can import the necessary modules and set up the mock server:

import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

// set up the mock server
const server = setupServer(
  http.get('https://api.tvmaze.com/shows/1', () => {
    return HttpResponse.json({ data: 'test data' })
  })
)

Here, we're using the setupServer function from msw to create a mock server. The http.get method allows us to define how the server should respond to a specific GET request. In this case, we're mocking a successful response for the /shows/1 endpoint, returning a JSON object with the data property set to 'test data'.

Next, we need to start the server before running our tests and stop it after the tests are complete:

// start the server before running tests
beforeAll(() => {
  server.listen()
})

// stop the server after running tests
afterAll(() => {
  server.close()
})

The beforeAll and afterAll hooks from the testing library (vitest in this example) ensure that the mock server is running during the test execution and is properly cleaned up afterwards.

Now, let's take a look at the test cases:

// test case 1: successful response
test('getData - success', async () => {
  const data = await getData('shows/1')
  expect(data).toEqual({ data: 'test data' })
})

In this test case, we're calling the getData function (which makes the API request using fetch) with the 'shows/1' endpoint. We then assert that the returned data matches the mocked response using the expect function from the testing library.

// test case 2: 404 (Not Found) error
test('getData - not found', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 404, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('Endpoint not found'))
})

In the second test case, we're mocking a 404 (Not Found) error response for the /shows/2 endpoint using the server.use method. We then call the getData function with the 'shows/2' endpoint and assert that the returned data matches the expected error.

// test case 3: 500 (Server Error)
test('getData - server error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 500, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('An error occurred while fetching data'))
})

Similar to the previous test case, we're mocking a 500 (Server Error) response for the /shows/2 endpoint and asserting that the returned data matches the expected error.

// test case 4: generic error response
test('getData - error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/fake', () => {
      return HttpResponse.error()
    })
  )
  const data = await getData('fake')
  expect(data).toMatchObject({ status: 'error' })
})

In the final test case, we're mocking a generic error response for the /fake endpoint using the HttpResponse.error() method. We then call the getData function with the 'fake' endpoint and assert that the returned data matches the expected error object.

By mocking the API responses with msw, we can test our code's behavior without relying on the actual TVMaze API, ensuring our tests are reliable and consistent across different environments.

I hope this step-by-step guide has helped you understand the importance of mocking and how to implement it using the msw library. Happy coding, and remember, writing tests is crucial for maintaining a robust and maintainable codebase!

Today, we're going to dive into the world of API testing with mocking, and I'll guide you through every step of the process. By the end of this post, you'll have a solid understanding of how to write robust tests without relying on external APIs, ensuring consistent and reliable results.

Before we begin, let's quickly go over some key concepts:

  • Mocking: Simulating the behavior of real objects or services within a controlled environment, allowing you to isolate and test specific parts of your codebase.

  • Mock Service Worker (MSW): A library that intercepts network requests and returns mocked responses, making it easier to test APIs without relying on actual servers.

Now, let's start by setting up the msw library. First, install it by running:

npm install msw --save-dev

Once installed, we can import the necessary modules and set up the mock server:

import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

// set up the mock server
const server = setupServer(
  http.get('https://api.tvmaze.com/shows/1', () => {
    return HttpResponse.json({ data: 'test data' })
  })
)

Here, we're using the setupServer function from msw to create a mock server. The http.get method allows us to define how the server should respond to a specific GET request. In this case, we're mocking a successful response for the /shows/1 endpoint, returning a JSON object with the data property set to 'test data'.

Next, we need to start the server before running our tests and stop it after the tests are complete:

// start the server before running tests
beforeAll(() => {
  server.listen()
})

// stop the server after running tests
afterAll(() => {
  server.close()
})

The beforeAll and afterAll hooks from the testing library (vitest in this example) ensure that the mock server is running during the test execution and is properly cleaned up afterwards.

Now, let's take a look at the test cases:

// test case 1: successful response
test('getData - success', async () => {
  const data = await getData('shows/1')
  expect(data).toEqual({ data: 'test data' })
})

In this test case, we're calling the getData function (which makes the API request using fetch) with the 'shows/1' endpoint. We then assert that the returned data matches the mocked response using the expect function from the testing library.

// test case 2: 404 (Not Found) error
test('getData - not found', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 404, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('Endpoint not found'))
})

In the second test case, we're mocking a 404 (Not Found) error response for the /shows/2 endpoint using the server.use method. We then call the getData function with the 'shows/2' endpoint and assert that the returned data matches the expected error.

// test case 3: 500 (Server Error)
test('getData - server error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 500, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('An error occurred while fetching data'))
})

Similar to the previous test case, we're mocking a 500 (Server Error) response for the /shows/2 endpoint and asserting that the returned data matches the expected error.

// test case 4: generic error response
test('getData - error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/fake', () => {
      return HttpResponse.error()
    })
  )
  const data = await getData('fake')
  expect(data).toMatchObject({ status: 'error' })
})

In the final test case, we're mocking a generic error response for the /fake endpoint using the HttpResponse.error() method. We then call the getData function with the 'fake' endpoint and assert that the returned data matches the expected error object.

By mocking the API responses with msw, we can test our code's behavior without relying on the actual TVMaze API, ensuring our tests are reliable and consistent across different environments.

I hope this step-by-step guide has helped you understand the importance of mocking and how to implement it using the msw library. Happy coding, and remember, writing tests is crucial for maintaining a robust and maintainable codebase!

Today, we're going to dive into the world of API testing with mocking, and I'll guide you through every step of the process. By the end of this post, you'll have a solid understanding of how to write robust tests without relying on external APIs, ensuring consistent and reliable results.

Before we begin, let's quickly go over some key concepts:

  • Mocking: Simulating the behavior of real objects or services within a controlled environment, allowing you to isolate and test specific parts of your codebase.

  • Mock Service Worker (MSW): A library that intercepts network requests and returns mocked responses, making it easier to test APIs without relying on actual servers.

Now, let's start by setting up the msw library. First, install it by running:

npm install msw --save-dev

Once installed, we can import the necessary modules and set up the mock server:

import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

// set up the mock server
const server = setupServer(
  http.get('https://api.tvmaze.com/shows/1', () => {
    return HttpResponse.json({ data: 'test data' })
  })
)

Here, we're using the setupServer function from msw to create a mock server. The http.get method allows us to define how the server should respond to a specific GET request. In this case, we're mocking a successful response for the /shows/1 endpoint, returning a JSON object with the data property set to 'test data'.

Next, we need to start the server before running our tests and stop it after the tests are complete:

// start the server before running tests
beforeAll(() => {
  server.listen()
})

// stop the server after running tests
afterAll(() => {
  server.close()
})

The beforeAll and afterAll hooks from the testing library (vitest in this example) ensure that the mock server is running during the test execution and is properly cleaned up afterwards.

Now, let's take a look at the test cases:

// test case 1: successful response
test('getData - success', async () => {
  const data = await getData('shows/1')
  expect(data).toEqual({ data: 'test data' })
})

In this test case, we're calling the getData function (which makes the API request using fetch) with the 'shows/1' endpoint. We then assert that the returned data matches the mocked response using the expect function from the testing library.

// test case 2: 404 (Not Found) error
test('getData - not found', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 404, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('Endpoint not found'))
})

In the second test case, we're mocking a 404 (Not Found) error response for the /shows/2 endpoint using the server.use method. We then call the getData function with the 'shows/2' endpoint and assert that the returned data matches the expected error.

// test case 3: 500 (Server Error)
test('getData - server error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 500, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('An error occurred while fetching data'))
})

Similar to the previous test case, we're mocking a 500 (Server Error) response for the /shows/2 endpoint and asserting that the returned data matches the expected error.

// test case 4: generic error response
test('getData - error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/fake', () => {
      return HttpResponse.error()
    })
  )
  const data = await getData('fake')
  expect(data).toMatchObject({ status: 'error' })
})

In the final test case, we're mocking a generic error response for the /fake endpoint using the HttpResponse.error() method. We then call the getData function with the 'fake' endpoint and assert that the returned data matches the expected error object.

By mocking the API responses with msw, we can test our code's behavior without relying on the actual TVMaze API, ensuring our tests are reliable and consistent across different environments.

I hope this step-by-step guide has helped you understand the importance of mocking and how to implement it using the msw library. Happy coding, and remember, writing tests is crucial for maintaining a robust and maintainable codebase!

Today, we're going to dive into the world of API testing with mocking, and I'll guide you through every step of the process. By the end of this post, you'll have a solid understanding of how to write robust tests without relying on external APIs, ensuring consistent and reliable results.

Before we begin, let's quickly go over some key concepts:

  • Mocking: Simulating the behavior of real objects or services within a controlled environment, allowing you to isolate and test specific parts of your codebase.

  • Mock Service Worker (MSW): A library that intercepts network requests and returns mocked responses, making it easier to test APIs without relying on actual servers.

Now, let's start by setting up the msw library. First, install it by running:

npm install msw --save-dev

Once installed, we can import the necessary modules and set up the mock server:

import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

// set up the mock server
const server = setupServer(
  http.get('https://api.tvmaze.com/shows/1', () => {
    return HttpResponse.json({ data: 'test data' })
  })
)

Here, we're using the setupServer function from msw to create a mock server. The http.get method allows us to define how the server should respond to a specific GET request. In this case, we're mocking a successful response for the /shows/1 endpoint, returning a JSON object with the data property set to 'test data'.

Next, we need to start the server before running our tests and stop it after the tests are complete:

// start the server before running tests
beforeAll(() => {
  server.listen()
})

// stop the server after running tests
afterAll(() => {
  server.close()
})

The beforeAll and afterAll hooks from the testing library (vitest in this example) ensure that the mock server is running during the test execution and is properly cleaned up afterwards.

Now, let's take a look at the test cases:

// test case 1: successful response
test('getData - success', async () => {
  const data = await getData('shows/1')
  expect(data).toEqual({ data: 'test data' })
})

In this test case, we're calling the getData function (which makes the API request using fetch) with the 'shows/1' endpoint. We then assert that the returned data matches the mocked response using the expect function from the testing library.

// test case 2: 404 (Not Found) error
test('getData - not found', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 404, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('Endpoint not found'))
})

In the second test case, we're mocking a 404 (Not Found) error response for the /shows/2 endpoint using the server.use method. We then call the getData function with the 'shows/2' endpoint and assert that the returned data matches the expected error.

// test case 3: 500 (Server Error)
test('getData - server error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/shows/2', () => {
      return new HttpResponse(null, { status: 500, statusText: 'Not Found' })
    })
  )
  const data = await getData('shows/2')
  expect(data).toEqual(new Error('An error occurred while fetching data'))
})

Similar to the previous test case, we're mocking a 500 (Server Error) response for the /shows/2 endpoint and asserting that the returned data matches the expected error.

// test case 4: generic error response
test('getData - error', async () => {
  server.use(
    http.get('https://api.tvmaze.com/fake', () => {
      return HttpResponse.error()
    })
  )
  const data = await getData('fake')
  expect(data).toMatchObject({ status: 'error' })
})

In the final test case, we're mocking a generic error response for the /fake endpoint using the HttpResponse.error() method. We then call the getData function with the 'fake' endpoint and assert that the returned data matches the expected error object.

By mocking the API responses with msw, we can test our code's behavior without relying on the actual TVMaze API, ensuring our tests are reliable and consistent across different environments.

I hope this step-by-step guide has helped you understand the importance of mocking and how to implement it using the msw library. Happy coding, and remember, writing tests is crucial for maintaining a robust and maintainable codebase!

Get in touch

Seeking a fresh opportunity or have an inquiry? Don't hesitate to reach out to me.

Get in touch

Seeking a fresh opportunity or have an inquiry? Don't hesitate to reach out to me.

Get in touch

Seeking a fresh opportunity or have an inquiry? Don't hesitate to reach out to me.

©

2024

Dylan Britz