Search code examples
unit-testingvue.jsjestjsaxiosvue-test-utils

Mocking axios in a vue3 typescript unit test using jest and vue-test-utils2 (Solved)


My component calls

this.axios.get()

when being mounted and passes a vuex-store variable to the api. The api returns an array as the response and the component displays some of the returned data after exchanging a loading-element with the real content.

In my unit test I want to simulate the result of the axios-request, wait for the transition between the loading- and the content-element and then finally check the validity of the content. However, the test fails and outputs:

Cannot read property 'get' of undefined

and highlights the get on this.axios.

Here is what I'm expecting to work (based on this guide):

... some imports etc. ...
const mockAxios = { whatIExpectToGet  };
jest.mock("axios", () => ({
  get: jest.fn(() => mockAxios)
}));
it("description of the test", async () => {
  const wrapper = mount(MyComponent);
... code continues ...

Of course I'm accesssing axios via this and not directly like they do in the guide. But, since I can't find any mention of anything related to that, I assume that's irrelevant?

I also tried to mock axios myself like so:

... imports etc. ...
const axios = {
  get: Promise.resolve({ whatIExpectToGet })
};
it("description of the test", async () => {
  const wrapper = mount(MyComponent, {
    global: {
      mocks: [ axios ]
    }
  });
... code continues ...

Apparently people with similar problems used localVue.use() to inject stuff, but that's no longer supported.

Could someone be so kind and smart as to point me into the right direction, please? Thank you.

-------------------> SOLUTION <-------------------

Thanks to tony 19 this question is already solved.

I ended up using an async function to mock axios because Promise.resolve() wasn't working for me:

import { shallowMount, flushPromises } from "@vue/test-utils";
import MyComponent from "@/components/MyComponent.vue";

describe("MyComponent.vue", () => {
  const axios = {
    get: async () => ({
      data: { expectedData }
    })
  };
  it("test description", async () => {
    const wrapper = shallowMount(MyComponent, {
      global: {
        mocks: {
          axios: axios
        }
      }
    } as any);
    expect(wrapper.html()).toContain("some_string_i_display_while_loading");
    await flushPromises();
    expect(wrapper.html()).toContain("some_string_i_display_after_getting_the_response");
  });
});

Solution

  • Using global.mocks to mock axios is the right approach, but your attempt incorrectly used an array when it should've been an object:

    const wrapper = mount(MyComponent, {
      global: {
        // mocks: [ axios ] ❌
        mocks: { axios } ✅
      }
    })
    

    Note axios.get() resolves to an axios.Response object, which stores the response data in its data property, so your mock should do the same.

    Here's a full example:

    // MyComponent.vue
    export default {
      mounted() {
        this.axios.get('foo').then(resp => this.foo = resp.data)
      }
    }
    
    // MyComponent.spec.js
    it('gets foo', () => {
      const wrapper = mount(MyComponent, {
        global: {
          mocks: {
            axios: {
              get: Promise.resolve({ data: { foo: true }})
    
              // OR use an async function, which internally returns a Promise
              get: async () => ({ data: { foo: true }})
            }
          }
        }
      }
    })