虽然测试过多或过少都是可能的,但我的观察是,开发人员通常会在测试过多时犯错误。毕竟,没有人愿意成为一个组件测试不足导致应用程序在生产中崩溃的人。
在本文中,我将与您分享一些用于单元测试组件的指导原则,这些指导原则确保我不会永远编写测试,但是提供了足够的覆盖率,使我不会遇到麻烦。
我假设您已经了解了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存储中的数据与插入的数据相同
如果计算的属性返回正确的项
如果路由器推送重定向到正确的页面
总结
我认为这三个相对简单的测试对于这个组件来说足够了。
除非另有证明,否则在单元测试组件进行测试时需要有良好的思维方式。
以下是您可以自问的问题:
这是业务逻辑的一部分吗?
这是否直接测试组件的输入和输出?
这是测试我的代码还是第三方代码?
英文原文地址:https://vuejsdevelopers.com/2019/08/26/vue-what-to-unit-test-components/
以上就是知道要测试什么——Vue组件单元测试的详细内容,更多请关注0133技术站其它相关文章!