跳至内容

发出 HTTP 请求

现代测试运行器在测试 HTTP 请求方面已经提供了许多很棒的功能。因此,Vue 测试工具没有提供任何独特的工具来做到这一点。

但是,这是一个重要的测试功能,我们想强调一些需要注意的地方。

在本节中,我们将探讨一些执行、模拟和断言 HTTP 请求的模式。

博客文章列表

让我们从一个基本用例开始。以下 PostList 组件渲染从外部 API 获取的博客文章列表。为了获取这些文章,该组件包含一个 button 元素,它会触发请求

vue
<template>
  <button @click="getPosts">Get posts</button>
  <ul>
    <li v-for="post in posts" :key="post.id" data-test="post">
      {{ post.title }}
    </li>
  </ul>
</template>

<script>
import axios from 'axios'

export default {
  data() {
    return {
      posts: null
    }
  },
  methods: {
    async getPosts() {
      this.posts = await axios.get('/api/posts')
    }
  }
}
</script>

我们需要做几件事才能正确测试此组件。

我们的第一个目标是测试此组件 **无需实际访问 API**。这将创建一个脆弱且可能很慢的测试。

其次,我们需要断言该组件使用适当的参数进行了正确的调用。我们不会从该 API 获取结果,但我们仍然需要确保我们请求了正确的资源。

此外,我们需要确保 DOM 已相应更新并显示数据。我们通过使用 @vue/test-utils 中的 flushPromises() 函数来做到这一点。

js
import { mount, flushPromises } from '@vue/test-utils'
import axios from 'axios'
import PostList from './PostList.vue'

const mockPostList = [
  { id: 1, title: 'title1' },
  { id: 2, title: 'title2' }
]

// Following lines tell Jest to mock any call to `axios.get`
// and to return `mockPostList` instead
jest.spyOn(axios, 'get').mockResolvedValue(mockPostList)

test('loads posts on button click', async () => {
  const wrapper = mount(PostList)

  await wrapper.get('button').trigger('click')

  // Let's assert that we've called axios.get the right amount of times and
  // with the right parameters.
  expect(axios.get).toHaveBeenCalledTimes(1)
  expect(axios.get).toHaveBeenCalledWith('/api/posts')

  // Wait until the DOM updates.
  await flushPromises()

  // Finally, we make sure we've rendered the content from the API.
  const posts = wrapper.findAll('[data-test="post"]')

  expect(posts).toHaveLength(2)
  expect(posts[0].text()).toContain('title1')
  expect(posts[1].text()).toContain('title2')
})

请注意,我们在变量 mockPostList 前添加了前缀 mock。如果没有,我们将收到错误:“jest.mock() 的模块工厂不允许引用任何超出范围的变量”。这是 jest 特定的,你可以阅读更多关于这种行为的信息 在他们的文档中

还要注意我们如何等待 flushPromises 然后与组件交互。我们这样做是为了确保在断言运行之前 DOM 已更新。

jest.mock() 的替代方案

在 Jest 中有几种设置模拟的方法。上面示例中使用的方法是最简单的。对于更强大的替代方案,你可能想查看 axios-mock-adaptermsw,等等。

断言加载状态

现在,这个 PostList 组件非常有用,但它缺少一些其他很棒的功能。让我们扩展它,使其在加载文章时显示一条花哨的消息!

此外,让我们在加载时禁用 <button> 元素。我们不希望用户在获取数据时不断发送请求!

vue
<template>
  <button :disabled="loading" @click="getPosts">Get posts</button>

  <p v-if="loading" role="alert">Loading your posts…</p>
  <ul v-else>
    <li v-for="post in posts" :key="post.id" data-test="post">
      {{ post.title }}
    </li>
  </ul>
</template>

<script>
import axios from 'axios'

export default {
  data() {
    return {
      posts: null,
      loading: null
    }
  },
  methods: {
    async getPosts() {
      this.loading = true

      this.posts = await axios.get('/api/posts')

      this.loading = null
    }
  }
}
</script>

让我们编写一个测试来断言所有与加载相关的元素都按时渲染。

js
test('displays loading state on button click', async () => {
  const wrapper = mount(PostList)

  // Notice that we run the following assertions before clicking on the button
  // Here, the component should be in a "not loading" state.
  expect(wrapper.find('[role="alert"]').exists()).toBe(false)
  expect(wrapper.get('button').attributes()).not.toHaveProperty('disabled')

  // Now let's trigger it as usual.
  await wrapper.get('button').trigger('click')

  // We assert for "Loading state" before flushing all promises.
  expect(wrapper.find('[role="alert"]').exists()).toBe(true)
  expect(wrapper.get('button').attributes()).toHaveProperty('disabled')

  // As we did before, wait until the DOM updates.
  await flushPromises()

  // After that, we're back at a "not loading" state.
  expect(wrapper.find('[role="alert"]').exists()).toBe(false)
  expect(wrapper.get('button').attributes()).not.toHaveProperty('disabled')
})

来自 Vuex 的 HTTP 请求

对于更复杂的应用程序,一个典型的场景是触发执行 HTTP 请求的 Vuex 操作。

这与上面概述的示例没有什么不同。我们可能希望按原样加载商店并模拟 axios 等服务。这样,我们就可以模拟系统的边界,从而在测试中获得更高的信心。

你可以查看 测试 Vuex 文档,以获取有关使用 Vue 测试工具测试 Vuex 的更多信息。

结论

  • Vue 测试工具不需要特殊的工具来测试 HTTP 请求。唯一需要考虑的是我们正在测试异步行为。
  • 测试不能依赖于外部服务。使用 jest.mock 等模拟工具来避免这种情况。
  • flushPromises() 是一个有用的工具,可以确保 DOM 在异步操作后更新。
  • 通过与组件交互直接触发 HTTP 请求可以使你的测试更具弹性。