How to Mock Axios with Vitest

When writing unit tests for services that utilize Axios as an HTTP client library. We need to mock it it’s not a good practice to do real requests in that environment. so How do we mock Axios?

In a nutshell, we have to mock the methods we use in axios, like .post and .get to return a promise that resolves with the data we specify using mockResolvedValue.

Let’s write some unit tests for an example service that uses Axios.

Example Users Service

As an example let’s imagine we have this service file that fetches and creates users.

import axios from 'axios'

const BASE_URL = 'https://jsonplaceholder.typicode.com'

export const fetchUsers = async () => {
  return (await axios.get(`${BASE_URL}/users`)).data
}

export const createUser = async (user) => {
  return (await axios.post(`${BASE_URL}/users`, user)).data
}

The fetchUsers function makes a GET request to fetch users. And the createUser function makes a POST request to create a new user. Both functions return the data from the response. Let’s develop unit tests for both of them.

Mock GET Requests

First, implement the unit test for the fetchUsers method. We expect that it calls axios.get with the proper URL and returns the data returned by it.

To get started, mock axios itself. Then, inside the test body mock the get method to return a promise with mock data.

import { beforeEach, describe, expect, test, vi } from 'vitest'
import { createUser, fetchUsers } from './users.service'
import axios from 'axios'

vi.mock('axios')

describe('Users Service', () => {
  describe('fetchUsers', () => {
    test('makes a GET request to fetch users', async () => {
      const usersMock = [{ id: 1 }, { id: 2 }]

      axios.get.mockResolvedValue({
        data: usersMock,
      })
    })
  })
})

Now if we call axios.get within our test, it will return a promise that resolves with the data. just as it would in our service file.

Afterward, call the function fetchUsers and expect that axios.get has been called with the proper URL:

describe('fetchUsers', () => {
  test('makes a GET request to fetch users', async () => {
    const usersMock = [{ id: 1 }, { id: 2 }]

    axios.get.mockResolvedValue({
      data: usersMock,
    })

    const users = await fetchUsers()

    expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/users')

  })
})

Next, expect that the users variable is strictly equal to the usersMock we provided earlier to mockResolvedValue:

describe('fetchUsers', () => {
  test('makes a GET request to fetch users', async () => {
    const usersMock = [{ id: 1 }, { id: 2 }]

    axios.get.mockResolvedValue({
      data: usersMock,
    })

    const users = await fetchUsers()

    expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/users')
    expect(users).toStrictEqual(usersMock)
  })
})

Finally, reset the mocked methods so that we have an empty call history before each test. This will help us evade false positives, where the method has been called in an earlier test while we expect it to be called in a current one:

import { beforeEach, describe, expect, test, vi } from 'vitest'
import { createUser, fetchUsers } from './users.service'
import axios from 'axios'

vi.mock('axios')

describe('Users Service', () => {
  beforeEach(() => {
    axios.get.mockReset()
  })

  describe('fetchUsers', () => {
    test('makes a GET request to fetch users', async () => {
      const usersMock = [{ id: 1 }, { id: 2 }]

      axios.get.mockResolvedValue({
        data: usersMock,
      })

      const users = await fetchUsers()

      expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/users')
      expect(users).toStrictEqual(usersMock)
    })
  })
})

If run the tests we should see that it passes:

vitest mock axios passing test

Mock POST Requests

Let’s now write the test for createUser. We expect it to call axios.post with a user payload and return the data from the response.

First, create the test case and mock the post method:

describe('createUser', () => {
  test('makes a POST request to create a new user', async () => {
    const newUserPayload = {
      name: 'john doe',
    }

    const newUserMock = {
      id: 1,
      ...newUserPayload,
    }

    axios.post.mockResolvedValue({
      data: newUserMock,
    })

    const newUser = await createUser(newUserPayload)
  })
})

Similar to what we did earlier, we use mockResolvedValue to return a response with a data attribute as the Axios do in the real implementation.

Next, add the expectations. Assert that the post method has been called and that the newUser object is strictly equal to the one we return from the mock

describe('createUser', () => {
  test('makes a POST request to create a new user', async () => {
    const newUserPayload = {
      name: 'john doe',
    }

    const newUserMock = {
      id: 1,
      ...newUserPayload,
    }

    axios.post.mockResolvedValue({
      data: newUserMock,
    })

    const newUser = await createUser(newUserPayload)

    expect(axios.post).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/users', newUserPayload)
    expect(newUser).toStrictEqual(newUserMock)
  })
})

Finally, reset the mocked post method also in the beforEach block and run the tests should be passing

beforeEach(() => {
  axios.get.mockReset()
  axios.post.mockReset()
})
vitest mock axios passing test

Conclusion

In this tutorial we learned how to mock axios with Vitest, I hope that helps you in your unit-testing journey.

This tutorial is part of a series that goes through mocking the commonly used Web APIs and Packages, you can find the others here. Also, you can find the code for this tutorial on Github. Cheers!

Amenallah Hsoumi
Amenallah Hsoumi

Senior Software Engineer, Indie Hacker, building cool stuff on the web!

Articles: 19