如何为我的VUE项目编写高效的单元测试--Jest
Unit Testing(单元测试)--Jest
一个完整的测试程序通常由几种不同的测试组合而成,比如end to end(E2E)测试,有时还包括整体测试、简要测试和单元测试。这里要介绍的是Vue中的单元测试,使用流行的Jest JavaScript测试库来运行我们的测试。
1.测试的目的
排除故障
每个应用的开发中,多少会出现一些意料之外的bug。通过测试应用程序,可以帮助我们大大减少此类问题,并增强应用程序的逻辑性。
保证团队成员的逻辑统一
如果您是团队的新成员,并且对应用程序还不熟悉,那么一组测试就好像是有经验的开发人员监视你编写代码,确保您处于代码应该执行的正确路线之内。通过这些测试,您可以确信在添加新功能或更改现有代码时不会破坏任何东西。
可以提高质量代码
当您在编写vue组件时,由于考虑到测试,最好的方案将是创建独立的、更可重用的组件。如果您开始为您的组件编写测试,并且您注意到这些组件不容易测试,那么您可能会重构您的组件,最终起到改进它们的效果。
起到很好的说明文档作用
正如在第一点中提到的,测试的另一个结果是,它最终可以为您的开发团队生成良好的文档。当某人对代码库还不熟悉时,他们可以查看测试以获得指导,这可以提供关于组件应该如何工作的意图的洞察,并为可能要测试的边缘部分提供线索。
1.我们需要测试什么
在测试的过程中,很容易照成过分的测试一些不需要的东西,这会不必要地减慢开发时间。那么我们在Vue.js应用程序中测试什么呢?答案其实很简单:组件。由于Vue应用程序是一个个组件相互组合而成的,我们需要测试它们各自的行为,以确保它们正常工作。
组件原理及分解
我们可以先分解组件,看看组件之间是怎么协调工作的。
一般来说,组件需要遵照我们定义的逻辑来工作,这里有一个输入和输出的概念。组件会接收一些静态或动态的输入值,然后依照逻辑输出一些值或者是dom元素。
正常来说,组件的输入输出有以下这些:
1// 输入值
2
3// 静态接收数据
4Component Data
5// 动态接收数据
6Component Props
7// 用户互交,例如:一个用户单击的按钮
8User Interaction
9// 生命周期逻辑,例如:mounted(), created()
10Lifecycle Methods
11// 组件状态值
12Vuex Store
13// 路由参数
14Route Params
15
16// 输出值
17
18// 输出的dom
19What is rendered to the DOM
20// 外部调用的函数
21External function calls
22// 组件触发的事件
23Events emitted by the component
24// 路由的变更
25Route Changes
26// 组件状态值得更新
27Updates to the Vuex Store
28// 对子组件的改变
29Connection with children
30
我们可以识别组件的输入和输出,挑选我们应该测试的内容,从而避开测试组件的内部业务逻辑。换句话说,我们不应该因为担心每一行代码如何工作而陷入困境。
这看起来可能有悖常理,但是单元测试的目标纯粹是确保组件产生预期的结果。我们在这里不关心它是如何得出这个结果的。后期我们甚至可能会改变我们的逻辑方式,所以我们不希望测试如何实现这些逻辑。这些逻辑不是测试的工作。就单元测试而言,我们只要确保组件的输出正常就行了。
要测试的部分
1<template>
2 <div>
3 <button v-show="loggedIn">Logout</button>
4 </div>
5</template>
6
7<script>
8export default {
9 data() {
10 return {
11 loggedIn: false
12 }
13 }
14}
15</script>
在这个例子中,我们有一个组件,如果loggedIn属性为true,它将显示一个logout按钮。为了弄清楚我们应该测试这个组件的哪个部分,我们的第一步是确定组件的输入和输出。
1input
2
3// 静态数据
4data => loggedIn
5// 这个数据属性决定按钮是否显示,所以这是一个我们应该测试的输入
6
7output
8
9Dom输出(button)
10根据输入(loggedIn),我们的按钮应该在什么时候显示在DOM中
对于更复杂的组件,将有更多的方面需要测试,但同样的方法也适用。
不测试的部分
对你应该测试的东西知道大概当然是必要的,知道你不应该测试的东西也是有帮助的。如下:
不测试组件逻辑的详情(implementation details)
当单元测试时,我们不需要为某些逻辑是如何工作而烦恼,只要它们确实工作就行了。我们不在乎这里的内部结构。我们只关心组件产生了我们期望的输出。
不要测试框架本身
开发人员经常试图测试太多,包括框架本身的内部工作。但是框架作者已经建立了这样的测试。
例如,props中的数据类型,如果我们尝试传入错误数据,Vue将抛出错误。我们不需要浪费时间测试Vue.js框架。这包括不要对Vue路由器和Vuex进行不必要的测试。
不测试第三方库
如果您使用的第三方库是高质量的,那么它们已经有了自己的测试。我们不需要测试它们的内部结构。例如,我们不需要测试Axios是如何工作的。Axios队为我们做了这件事。
2.编写单元测试
当我们使用Vue CLI创建应用项目时:
- 首先需要选择Manually select features自定义一些项目配置
- 然后在选择项目依赖时,勾选Unit Testing选项
- 最后在选择测试方案时。选择Jest
- vue/cli项目可以这样安装
1vue add @vue/unit-jest
2
3 Successfully installed plugin: @vue/cli-plugin-unit-jest
4// 如果安装的jest运行报错可能是版本问题,可以尝试npm update
这样创建的项目中就为我们安装好了依赖。
打开项目后,让我们从查看package.json开始,在这里我们将看到为我们安装了Jest和vue测试实用程序。
1// package.json
2"devDependencies": {
3 "@vue/cli-plugin-unit-jest": "^3.11.0",
4 "@vue/test-utils": "1.0.0-beta.29"
5}
Jest是一个JavaScript测试框架,它专注于简化单元测试。Jest将为我们运行单元测试,并在测试通过或失败时向我们报告。
API list:
1afterAll(fn, timeout)
2afterEach(fn, timeout)
3beforeAll(fn, timeout)
4beforeEach(fn, timeout)
5describe(name, fn)
6describe.each(table)(name, fn, timeout)
7describe.only(name, fn)
8describe.only.each(table)(name, fn)
9describe.skip(name, fn)
10describe.skip.each(table)(name, fn)
11test(name, fn, timeout)
12test.each(table)(name, fn, timeout)
13test.only(name, fn, timeout)
14test.only.each(table)(name, fn)
15test.skip(name, fn)
16test.skip.each(table)(name, fn)
17test.todo(name)
@vue/test-utils是Vue.js的官方单元测试实用程序库。它使我们能够在测试中渲染组件,然后对这些渲染的组件执行各种操作。这使得我们可以测试组件的运行结果。
如何运行Jest测试,我们来查看package.json中的运行脚本
1"scripts": {
2 ...
3 "test:unit": "vue-cli-service test:unit"
4}
5// 在我们编写好测试代码后只要运行:
6npm run test:unit
7// 或者在CLI中运行测试就可以运行测试代码
8// 这个命令的意思就是查看tests/unit目录,并运行目录下名为[componentsName].spec.js的文件。
查看项目根目录下的tests/unit文件夹,有一个初始化的Example.spec.js文件。spec是specification的缩写,即详述组件运行规则的js文件
- ## demo1(测试一个方法)
创建函数文件demo1.js
1function sum(a, b) {
2 return a + b;
3}
4module.exports = sum;
在tests/unit/中创建demo1.spec.js
1// 导入要测试的文件
2const sum = require('./sum')
3
4// 使用test(name, fn, timeout)创建一个测试
5test('adds 1 + 2 to equal 3', () => {
6 expect(sum(1, 2)).toBe(3)
7})
8
9// 其中,name是对测试的描述,测试结果会打印
10// fn是要执行的测试
11// expect()方法中描述的是测试的结果
12// toBe()是测试预期的值
运行这个单元测试
1npm run test:unit
2
3PASS tests/unit/demo1.spec.js
4✓ adds 1 + 2 to equal 3 (5ms)
5// PASS表示测试通过,函数运行正常
6// 下面列出每一项text(),[test.name (测试使用的时间)]
- ## demo2(测试一个组件)
创建组件demo2.vue
1<template>
2 <div>
3 <button v-show="loggedIn">Logout</button>
4 </div>
5</template>
6<script>
7export default {
8 data() {
9 return {
10 loggedIn: false
11 }
12 }
13}
14</script>
关于这个组件,前面我们已经介绍过了,要测试的点如下:
- 如果用户未登录,则不显示“注销”按钮
- 如果用户已登录,则显示“注销”按钮
在tests/unit/中创建demo2.spec.js
1// 导入要测试的组件
2import demo2 from '@/components/demo2'
3
4// 因为我们要测试的是组件,所以我们在测试时需要加载这个组件
5// `@vue/test-utils`为我们提供了这项功能
6import { mount } from '@vue/test-utils'
7// 使用describe(name, fn)创建一个测试组
8// 当我们有多个测试时,用这种方式组织它们比较具有逻辑性
9
10describe('ComponentsDemo2', () => {
11
12 // 其中写所有组件内需要的测试
13 test('if user is not logged in, do not show logout button', () => {
14 // 安装组件
15 const wrapper = mount(AppHeader)
16
17 // 默认登录状态是false,则找到组件内的btn,查看其可见性为false
18 expect(wrapper.find('button').isVisible()).toBe(false)
19 })
20 test('if a user is logged in, show logout button', () => {
21 // 安装组件
22 const wrapper = mount(AppHeader)
23
24 // 要测试登录状态是‘已登录’,则先设置其状态值
25 wrapper.setData({ loggedIn: true })
26
27 // 这时找到组件内的btn,查看其可见性为true
28 expect(wrapper.find('button').isVisible()).toBe(true)
29 })
30})
提示:
您可能还会看到使用it()的测试块,它是test()的别名。
@vue/test-utils,还提供shallowMount()方法。如果组件有子组件,shallowMount()将返回该组件的本身,而不是完全渲染的详细组件单元测试的焦点是单一的组件,多数时候我们会忽略其子组件。
运行这个单元测试
1npm run test:unit
2
3PASS tests/unit/demo2.spec.js
4ComponentsDemo2
5 ✓ if user is not logged in, do not show logout button (20ms)
6 ✓ if a user is logged in, show logout button (30ms)
7
8Test Suites: 1 passed, 1 total
9Tests: 2 passed, 2 total
10Snapshots: 0 total
11Time: 2.653s
12Ran all test suites.
步骤总结如下图:
- 创建一个测试组:describe()
- 开始一个测试:test()
- 安装渲染组件:mount()
- 必要的时候设置组件参数:setData()
- 调试预期的组件行为及数据正确与否:expect()
长按二维码关注公众号
如何为我的VUE项目编写高效的单元测试--Jest的更多相关文章
- TypeScript编写Vue项目结构解析
使用TypeScript编写Vue项目也已经有了一段时间,笔者在刚刚使用TypeScript时候也是很茫然,不知道从何下手,感觉使用TypeScript写项目感觉很累赘并不像JavaScript那么灵 ...
- 用webstorm搭建vue项目
本文只针对新手. 首先要明白几个名词(概念). Node.js: Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻塞式 ...
- 前端Vue项目——首页/课程页面开发及Axios请求
一.首页轮播图 1.elementUI走马灯 elementUI中 Carousel 走马灯,可以在有限空间内,循环播放同一类型的图片.文字等内容. 这里使用指示器样式,可以将指示器的显示位置设置在容 ...
- MySql如何编写高效的SQL
最近应团队要求,研究整理了下,mysql相关的优化,有些是根据实际java项目中碰到的情况经验之谈.欢迎讨论~ SQL 语言是一种强大而且灵活的语言,在使用 SQL 语言来执行某个关系查询的时候,用户 ...
- Vue 项目实战系列 (一)
最近一直在学习Vue,基本的文档看完后就需要进行具体的项目进行练手了,本系列文章主要是将我学习过程记录下来,和大家一起学习交流. 我在git上找到了一个淘票票的Vue项目,项目地址: https:// ...
- vue项目构建与实战
关于 微信公众号:前端呼啦圈(Love-FED) 我的博客:劳卜的博客 知乎专栏:前端呼啦圈 前言 由于vue相对来说比较平缓的学习过程和新颖的技术思路,使其受到了广大前后端开发者的青睐,同时其通俗易 ...
- vue插件编写与实战
关于 微信公众号:前端呼啦圈(Love-FED) 我的博客:劳卜的博客 知乎专栏:前端呼啦圈 前言 热爱vue开发的同学肯定知道awesome-vue 这个github地址,里面包含了数以千计的vue ...
- 手把手教你用vue-cli搭建vue项目
手把手教你用vue-cli搭建vue项目 本篇主要是利用vue-cli来搭建vue项目,其中前提是node和npm已经安装好,文章结尾将会简单提到一个简单的例子.使用vue-cli搭建项目最开始我也是 ...
- webpack+vue项目实战(四,前端与后端的数据交互和前端展示数据)
地址:https://segmentfault.com/a/1190000010063757 1.前言 今天要做的,就是在上一篇文章的基础上,进行功能页面的开发.简单点说呢,就是与后端的数据交互和怎么 ...
随机推荐
- [MySQL]MySQL8.0的一些注意事项以及解决方案
MySQL8.0 注意事项以及解决方案 1. MySQL8.0 修改大小写敏感配置 天坑MySQL8.0! 在安装后, 便无法通过修改配置文件,重启服务,或者执行sql来更改数据库配置, 要想配置的话 ...
- huawei 华为 ubuntu mysql 访问不了的原因
标签:服务器 ins 查看 tar 安全组 service 一. 首先将3306端口添加至安全组, 确保端口没有被封掉 二. 开启服务器上的mysql 权限, 步骤如下 1.mys ...
- 无法更新apt镜像源?树莓派安装最新版Debian11(bullseye)arm64位系统步骤
镜像下载.域名解析.时间同步请点击阿里云开源镜像站 树莓派系统在2022年1月28日迎来了更新,更新了64位的系统,同时也有lite系统供我们使用.32位系统很多软件都用不了,特别是宝塔面板.下面是安 ...
- docker学习笔记(4)- 应用数据管理(容器外)
简介 docker storage driver支持了image分层存储和容器可写层的存储管理,使用挂载主机目录的方式可以将数据存储在主机的文件系统上或内存中. 之前学习过镜像的分层存储,以Docke ...
- SVPWM实现概述
SVPWM是FOC的基础,其实现流程大致如下所示: 1. 判断合成矢量所在扇区 2. 计算相邻矢量作用时间 3. 计算各桥臂导通时间 4. 得到各相PWM占空比 5. 更新相应寄存器值 SVPWM目 ...
- 记-beego项目调用Jenkins API获取job信息
type JenkinsController struct { beego.Controller } type Job struct { Name string `json:"name&qu ...
- java == 和 equals
- 什么是 Spring MVC 框架的控制器?
控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现.控制器解 析用户输入并将其转换为一个由视图呈现给用户的模型.Spring 用一个非常抽象 的方式实现了一个控制层,允许用户创建多种用途的控 ...
- redis主从复制与哨兵高可用
redis主从复制 话不多说,直接看案例: 环境准备, 主从规划 主节点:6380 从节点:6381.6382 运行3个redis数据库,达到 1主 2从的配置 #主库 6379.conf port ...
- 学习zabbix(六)
实验环境 实验用2到2台机器,实验所用机器系统环境如下,可以看到2台机器的主机名和IP地址 ? 1 2 3 4 5 6 7 8 9 10 [root@linux-node1 ~]# cat /etc/ ...