Categories
Vue 3 Testing

Testing Vue 3 Apps — Mock HTTP Requests

Spread the love

With apps getting more complex than ever, it’s important to test them automatically. We can do this with unit tests, and then we don’t have to test everything by hand.

In this article, we’ll look at how to test Vue 3 apps by writing a simple app and testing it.

Resolving Other Asynchronous Behavior

If we have async behavior in our components that are external to the component, then we a mock them.

For example, we can write:

Foo.vue

<template>
  <div>
    <p>{{ answer }}</p>
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      answer: "",
    };
  },
  beforeMount() {
    this.load();
  },
  methods: {
    async load() {
      const {
        data: { answer },
      } = await axios.get("https://yesno.wtf/api");
      this.answer = answer;
    },
  },
};
</script>

example.spec.js

import { mount } from 'vue-test-utils'
import flushPromises from 'flush-promises'
import axios from 'axios'
import Foo from './Foo'

jest.mock('axios', () => ({
  get: jest.fn(() => Promise.resolve({ data: { answer: 'yes' } }))
}))

test('uses a mocked axios HTTP client and flush-promises', async () => {
  const wrapper = mount(Foo)
  expect(axios.get).toHaveBeenCalledWith('https://yesno.wtf/api')
  await flushPromises()
  const div = wrapper.find('div')
  expect(div.text()).toContain('yes')
});

We have the Foo component that gets some data from the API and render it.

Then in example.spec.js , we mock the axios.get method with the jest.fn method.

It lets us spy on whether the function is called and what it’s called with.

Also, we return the data from the function.

Then in the test, we mount the component.

And then we call expect to check that axios.get is called with ‘https://yesno.wtf/api' like we did in Foo .

Then we call flushPromises to wait for the DOM to update.

And finally, we get the div and check the text with the text method.

Asserting Loading State

We can check the loading state also with our tests.

For example, we can write:

Foo.vue

<template>
  <div>
    <p v-if="loading" role="alert">Loading...</p>
    <p v-else>{{ answer }}</p>
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      loading: false,
      answer: "",
    };
  },
  beforeMount() {
    this.load();
  },
  methods: {
    async load() {
      this.loading = true;
      const {
        data: { answer },
      } = await axios.get("https://yesno.wtf/api");
      this.answer = answer;
      this.loading = false;
    },
  },
};
</script>

example.spec.js

import { mount } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import axios from 'axios'
import Foo from './Foo'

jest.mock('axios', () => ({
  get: jest.fn(() => Promise.resolve({ data: { answer: 'yes' } }))
}))

test('uses a mocked axios HTTP client and flush-promises', async () => {
  const wrapper = mount(Foo)
  expect(wrapper.find('[role="alert"]').exists()).toBe(true)
  expect(wrapper.find('[role="alert"]').text()).toBe('Loading...')
  expect(axios.get).toHaveBeenCalledWith('https://yesno.wtf/api')
  await flushPromises()
  const div = wrapper.find('div')
  expect(div.text()).toContain('yes')
  expect(wrapper.find('[role="alert"]').exists()).toBe(false)
});

We have the Foo component that has the loading message.

It’s controlled by the loading reactive property.

Then in the test, we check if the element with the role set to alert is rendered.

And then we call flushPromises and check that the element with the role set to alert is no longer rendered.

We also check that we get the response rendered as we did with the previous example.

Conclusion

We check mock HTTP requests to test our component with Vue 3 components and Vue Test Utils.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

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