关于单元测试 vue 组件, 我看到的最常见的问题是 "我到底应该测试什么?"
虽然测试过多或过少都是可能的, 但我的观察是, 开发人员通常会在测试过多时犯错误. 毕竟, 没有人愿意成为一个组件测试不足导致应用程序在生产中崩溃的人.
在本文中, 我将与您分享一些用于单元测试组件的指导原则, 这些指导原则确保我不会永远编写测试, 但是提供了足够的覆盖率, 使我不会遇到麻烦.
我假设您已经了解了 Jest 和 Vue 测试 Utils.
示例组件
在学习指导原则之前, 让我们首先熟悉我们将要测试的以下示例组件. 它叫做 Item.vue, 是电子商务应用中的产品项目.
Item.vue
- <template>
- <div>
- <h2>{{ item.title }}</h2>
- <button @click="addToCart">Add To Cart</button>
- <img :src="item.image"/>
- </div>
- </template>
- <script>
- export default {
- name: "Item",
- props: [ "id" ],
- computed: {
- item () {
- return this.$store.state.find(
- item => item.id === this.id
- );
- }
- },
- methods: {
- addToCart () {
- if (this.$auth.check()) {
- this.$store.commit("ADD_TO_CART", this.id);
- } else {
- this.$router.push({ name: "login" });
- }
- }
- }
- };
- </script>
规范文件设置
下面是测试的规范文件. 在其中, 我们将使用 Vue Test Utils 浅挂载组件, 因此我已经导入了它, 以及我们正在测试的 Item 组件.
我还创建了一个工厂函数, 它将生成一个可覆盖的配置对象, 这样我们就不必在每个测试中指定道具和模拟三个依赖项.
item.spec.JS
- import { shallowMount } from "@vue/test-utils";
- import Item from "@/components/Item";
- function createConfig (overrides) {
- const id = 1;
- const mocks = {
- // Vue Auth
- $auth: {
- check: () => false
- },
- // Vue Router
- $router: {
- push: () => {}
- },
- // Vuex
- $store: {
- state: [ { id } ],
- commit: () => {}
- }
- };
- const propsData = { id };
- return Object.assign({ mocks, propsData }, overrides);
- }
- describe("Item.vue", () => {
- // Tests go here
- });
确定业务逻辑
关于要测试的组件, 要问的第一个也是最重要的问题是 "什么是业务逻辑?" 换句话说, 组件的作用是什么?
对于 Item.vue, 这是业务逻辑:
它将根据接收到的 id 道具显示一个项目
如果用户是访客, 单击 Add to Cart 按钮将其重定向到登录页面
如果用户已登录, 单击 Add to Cart 按钮将触发 Vuex 突变 ADD_TO_CART
识别输入和输出
当您对组件进行单元测试时, 您将其视为一个黑盒. 方法, 计算属性等中的内部逻辑只影响输出.
所以, 下一个重要的事情是确定组件的输入和输出, 因为这些也是测试的输入和输出.
在 Item.vue 的情况下, 输入是:
id 道具
来自 Vuex 和 Vue Auth 的声明
用户通过单击按钮输入
而输出为:
呈现标记
数据发送到 Vuex 突变或 Vue 路由器推送
有些组件还可以将表单和事件作为输入, 并将事件作为输出发出.
测试 1: 当客户单击按钮时调用路由器
一个业务逻辑是 "如果用户是访客, 单击添加到购物车按钮会将他们重定向到登录页面". 让我们为此写一个测试.
我们将通过浅层安装组件来设置测试, 然后找到并单击 Add to Cart 按钮.
- test("router called when guest clicks button", () => {
- const config = createConfig();
- const wrapper = shallowMount(Item, config);
- wrapper
- .find("button")
- .trigger("click");
- // Assertion goes here
- }
我们稍后将添加一个断言.
不要超出输入和输出的边界
在这个测试中, 我们很有可能会在单击按钮后检查路由是否更改为登录页面的路由, 例如.
- import router from "router";
- test("router called when guest clicks button", () => {
- ...
- // Wrong
- const route = router.find(route => route.name === "login");
- expect(wrapper.vm.$route.path).toBe(route.path);
- }
虽然这确实隐式地测试了组件输出, 但是它依赖于路由器来工作, 这不应该是这个组件所关心的.
最好直接测试这个组件的输出, 即对 $router.push 的调用. 路由器是否完成该操作超出了此特定测试的范围.
因此, 让我们监视路由器的 push 方法, 并断言它是用 login route 对象调用的.
- import router from "router";
- test("router called when guest clicks button", () => {
- ...
- jest.spyOn(config.mocks.$router, "push");
- const route = router.find(route => route.name === "login");
- expect(spy).toHaveBeenCalledWith(route);
- }
测试 2:vuex 在 auth 用户单击按钮时调用
接下来, 让我们测试 "如果用户已登录, 单击 Add to Cart 按钮将触发 Vuex 突变 ADD_TO_CART" 的业务逻辑.
要重复上面的教训, 您不需要检查 Vuex 状态是否被修改. 我们将对 Vuex 商店进行单独的测试来验证这一点.
这个组件的工作就是提交, 所以我们只需要测试它就可以了.
首先重写 $auth. 检查 mock, 使其返回 true(对于已登录的用户也是如此). 然后, 我们将监视存储的提交方法, 并断言它是在单击按钮后调用的.
- test("vuex called when auth user clicks button", () => {
- const config = createConfig({
- mocks: {
- $auth: {
- check: () => true
- }
- }
- });
- const spy = jest.spyOn(config.mocks.$store, "commit");
- const wrapper = shallowMount(Item, config);
- wrapper
- .find("button")
- .trigger("click");
- expect(spy).toHaveBeenCalled();
- }
不要测试其他库的功能
Item 组件显示存储项的数据, 特别是标题和图像. 也许我们应该写一个测试来专门检查这些? 例如:
- test("renders correctly", () => {
- const wrapper = shallowMount(Item, createConfig());
- // Wrong
- expect(wrapper.find("h2").text()).toBe(item.title);
- }
这是另一个不必要的测试, 因为它只是测试 Vue 从 Vuex 中获取数据并将其插入到模板中的能力. Vue 库已经对该机制进行了测试, 所以您应该依赖于它.
测试 3: 正确渲染
但是如果有人不小心将 title 重命名为 name, 然后忘记更新插值表达式怎么办? 这难道不值得测试吗?
是的, 但如果你像这样测试模板的每个方面, 你会在哪里停止?
测试标记的最佳方法是使用快照测试来检查整体渲染输出. 这不仅包括标题插值, 还包括图像, 按钮文本, 任何类等.
- test("renders correctly", () => {
- const wrapper = shallowMount(Item, createConfig());
- expect(wrapper).toMatchSnapshot();
- });
下面是一些其他不需要测试的例子:
如果 src 属性绑定到 img 元素
如果添加到 Vuex 存储中的数据与插入的数据相同
如果计算的属性返回正确的项
如果路由器推送重定向到正确的页面
总结
我认为这三个相对简单的测试对于这个组件来说足够了.
除非另有证明, 否则在单元测试组件进行测试时需要有良好的思维方式.
以下是您可以自问的问题:
这是业务逻辑的一部分吗?
这是否直接测试组件的输入和输出?
这是测试我的代码还是第三方代码?
来源: http://www.css88.com/web/vue-js/12149.html