Appearance
编写易于测试的组件
¥Write components that are easy to test
Vue Test Utils 可帮助你为 Vue 组件编写测试。然而,VTU 能做的就只有这么多了。
¥Vue Test Utils helps you write tests for Vue components. However, there's only so much VTU can do.
以下是编写更易于测试的代码以及编写有意义且易于维护的测试的建议列表。
¥Following is a list of suggestions to write code that is easier to test, and to write tests that are meaningful and simple to maintain.
以下列表提供了一般指导,在常见情况下可能会派上用场。
¥The following list provide general guidance and it might come in handy in common scenarios.
不要测试实现细节
¥Do not test implementation details
从用户的角度考虑输入和输出。粗略地说,这是为 Vue 组件编写测试时应该考虑的所有事项:
¥Think in terms of inputs and outputs from a user perspective. Roughly, this is everything you should take into account when writing a test for a Vue component:
输入 | 示例 |
---|---|
互动 | 单击、输入...任何 "human" 交互 |
属性 | 组件接收的参数 |
数据流 | 来自 API 调用、数据订阅的数据传入... |
输出 | 示例 |
---|---|
DOM 元素 | 渲染给文档的任何可观察节点 |
事件 | 触发的事件(使用 $emit ) |
副作用 | 比如 console.log 或者 API 调用 |
其他一切都是实现细节。
¥Everything else is implementation details.
请注意,此列表不包含内部方法、中间状态甚至数据等元素。
¥Notice how this list does not include elements such as internal methods, intermediate states or even data.
经验法则是,测试不应在重构时中断,即当我们更改其内部实现而不更改其行为时。如果发生这种情况,测试可能依赖于实现细节。
¥The rule of thumb is that a test should not break on a refactor, that is, when we change its internal implementation without changing its behavior. If that happens, the test might rely on implementation details.
例如,我们假设一个基本的 Counter 组件,具有一个用于递增计数器的按钮:
¥For example, let's assume a basic Counter component that features a button to increment a counter:
vue
<template>
<p class="paragraph">Times clicked: {{ count }}</p>
<button @click="increment">increment</button>
</template>
<script>
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
}
</script>
我们可以编写以下测试:
¥We could write the following test:
js
import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'
test('counter text updates', async () => {
const wrapper = mount(Counter)
const paragraph = wrapper.find('.paragraph')
expect(paragraph.text()).toBe('Times clicked: 0')
await wrapper.setData({ count: 2 })
expect(paragraph.text()).toBe('Times clicked: 2')
})
请注意,我们在这里如何更新其内部数据,并且我们还依赖于 CSS 类等详细信息(从用户角度来看)。
¥Notice how here we're updating its internal data, and we also rely on details (from a user perspective) such as CSS classes.
提示
请注意,更改数据或 CSS 类名都会导致测试失败。不过,该组件仍将按预期工作。这称为误报。
¥Notice that changing either the data or the CSS class name would make the test fail. The component would still work as expected, though. This is known as a false positive.
相反,以下测试尝试坚持上面列出的输入和输出:
¥Instead, the following test tries to stick with the inputs and outputs listed above:
js
import { mount } from '@vue/test-utils'
test('text updates on clicking', async () => {
const wrapper = mount(Counter)
expect(wrapper.text()).toContain('Times clicked: 0')
const button = wrapper.find('button')
await button.trigger('click')
await button.trigger('click')
expect(wrapper.text()).toContain('Times clicked: 2')
})
像 Vue 测试库 这样的库就是建立在这些原则之上的。如果你对这种方法感兴趣,请务必检查一下。
¥Libraries such as Vue Testing Library are build upon these principles. If you are interested in this approach, make sure you check it out.
构建更小、更简单的组件
¥Build smaller, simpler components
一般的经验法则是,如果一个组件做得更少,那么它会更容易测试。
¥A general rule of thumb is that if a component does less, then it will be easier to test.
制作更小的组件将使它们更易于组合且更易于理解。以下是使组件更简单的建议列表。
¥Making smaller components will make them more composable and easier to understand. Following is a list of suggestions to make components simpler.
提取 API 调用
¥Extract API calls
通常,你将在整个应用中执行多个 HTTP 请求。从测试的角度来看,HTTP 请求向组件提供输入,组件也可以发送 HTTP 请求。
¥Usually, you will perform several HTTP requests throughout your application. From a testing perspective, HTTP requests provide inputs to the component, and a component can also send HTTP requests.
提示
如果你不熟悉测试 API 调用,请查看 触发 HTTP 请求 指南。
¥Check out the Making HTTP requests guide if you are unfamiliar with testing API calls.
提取复杂的方法
¥Extract complex methods
有时,组件可能具有复杂的方法、执行繁重的计算或使用多个依赖。
¥Sometimes a component might feature a complex method, perform heavy calculations, or use several dependencies.
这里的建议是提取这个方法并将其导入到组件中。这样,你可以使用 Jest 或任何其他测试运行程序单独测试该方法。
¥The suggestion here is to extract this method and import it to the component. This way, you can test the method in isolation using Jest or any other test runner.
这样做的另一个好处是最终会得到一个更容易理解的组件,因为复杂的逻辑被封装在另一个文件中。
¥This has the additional benefit of ending up with a component that's easier to understand because complex logic is encapsulated in another file.
此外,如果复杂的方法难以设置或速度缓慢,你可能需要模拟它以使测试更简单、更快。触发 HTTP 请求 上的示例是一个很好的例子 - axios 是一个相当复杂的库!
¥Also, if the complex method is hard to set up or slow, you might want to mock it to make the test simpler and faster. Examples on making HTTP requests is a good example – axios is quite a complex library!
在编写组件之前编写测试
¥Write tests before writing the component
如果事先编写测试,就无法编写不可测试的代码!
¥You can't write untestable code if you write tests beforehand!
我们的 速成课 提供了一个示例,说明如何在代码之前编写测试来生成可测试的组件。它还可以帮助你检测和测试边缘情况。
¥Our Crash Course offers an example of how writing tests before code leads to testable components. It also helps you detect and test edge cases.