前言
最近在看前端自动化测试相关的东西,在搭建环境的时候发现还是有许多需要注意的地方,而且网上很少有将各种测试(单元测试,集成测试,端对端测试)的环境搭建都提及的文章,对像我这样的新手不太友好,于是便打算删繁就简,希望通过这一篇文章能让大家对Vue
项目中自动化测试环境的搭建有个初步的认识,并且能够实现简单的测试用例,主要内容有:
- 单元测试环境搭建与案例实践(非浏览器环境)* 使用
Jest
测试工具函数* 使用Jest
+Vue Test Utils
测试Vue
组件 - 端对端测试环境搭建与案例实践(浏览器环境)* 使用
Cypress
测试页面
之所以没提及集成测试,是因为对前端而言,很难区分出单元测试和集成测试,普遍认为前端中的单元测试包括了集成测试,有关三种测试的区别,通过下面一张表希望能让大家对单元/集成/端对端测试有个初步的认识,感兴趣的可以继续了解一下测试金字塔和奖杯模型。
单元测试 | 集成测试 | 端对端测试 | |
---|---|---|---|
测试对象 | 代码单元(函数,模块等) | 组织在一起的代码单元 | 整个页面或应用 |
作用 | 保证代码单元在给给定输入下得到预期结果 | 保证多个模块组织在一起可以正常工作 | 模拟用户在真实环境中的操作,确保一切正常 |
测试环境 | 非浏览器环境 | 非浏览器环境 | 浏览器环境 |
代表工具 | Jest,Mocha,Karma等 | Jest, Testing Library等 | Cypress,Puppeteer, NightWatch等 |
本文所用的框架是vue2
,测试工具有Jest,Vue Test Utils和Cypress,选择它们的原因很简单:
图片来自于The State of JS 2021: 测试工具
好吧,其实是因为:
- Jest👉All in one, 开箱即用* Vue Test Utils👉官网推荐* Cypress👉All in one, 开箱即用* * *
在开始之前,我们需要有一个新的vue2
项目,这个相信大家有“手”(脚手架的手)就行,直接vue create first_test
。这里选择的是默认安装,也可以利用脚手架安装测试框架,但这不太现实,因为我们往往是对一个已有的项目进行测试,而不是在开发阶段就已经考虑到了测试🤣🤣🤣
接下来我们一起在一个已有项目(这里是vue2
新项目)中引入Jest
和Cypress
单元测试环境搭建与案例实践
安装 Jest
运行npm install jest --save-dev
,也可以使用简化的命令npm i jest -D
"devDependencies": {
..."jest": "^29.1.2",...
},
函数测试实践
在项目根目录下新建tests/unit
文件夹,用于存放单元测试的执行文件;在src
目录下新建utils
文件夹,用于存放工具函数。
F:.
│--src
│ │--utils
|sum.js
│--tests
| │--unit
| first_unit.spec.js
编写第一个单元测试用例first_unit.spec.js
,来测试一个简单的求和函数sum.js
,代码如下
// front_test/src/utils/sum.js
function sum(a, b) {return a + b;
}
module.exports = sum;
// front_test/tests/unit/first_unit.spec.js
const sum = require("../../src/utils/sum");
describe("first jest test", () => {it("test 1", () => {expect(sum(1, 2)).toBe(3);});it("test 2", () => {expect(sum(Infinity, Infinity)).toBe(Infinity);});
});
在package.json
中配置启动命令
"scripts": {"test:unit": "jest"},
然后在命令行输入npm run test:unit first_unit.spec.js
,出现绿色的PASS
就说明测试通过了🍻🍻🍻
Jest 常用配置
- 配置Babel来让Jest支持ES6语法*
npm install --save-dev babel-jest @babel/core @babel/preset-env babel-core@^7.0.0-bridge.0
* 在babel.config.js
中作如下配置module.exports = {presets: ["@babel/preset-env"],};
* 在项目根目录下新建jest的配置文件jest.config.js
,作如下配置module.exports = {transform: {"\\.[jt]sx?$": "babel-jest", },};
* 配置别名* 在jest.config.js
中添加如下配置module.exports = {moduleNameMapper: {"^@/(.*)$": "<rootDir>/src/$1",},};
* 以上配置完成后,first_unit.test.js
中的const sum = require("../../src/utils/sum");
就可以写成import sum from "@/utils/sum.js"
了其它的诸如支持typescript、生成覆盖率文件、使用webpack等的配置方法可以根据自己的需要参考官网
Vue Test Utils 的安装与配置
使用Jest
可以进行DOM测试、异步代码测试和快照测试等,但正如Jest本身的定位——一个令人愉快的javascript
测试框架。也就是.js
文件,所以要想对.vue
文件进行测试,还需要安装Vue
官方提供的Vue Test Utils
库,按官方的说法就是,它“是偏底层的组件测试库,可以提供对 Vue
特定 API 的访问”
运行npm install --save-dev @vue/test-utils@1 vue-jest
,由于是vue2的项目,所以这里指定了@vue/test-utils
的版本为v1
然后在jest.config.js
中作如下配置
module.exports = {moduleFileExtensions: ["js", "json", "vue"],transform: {".*\\.(vue)$": "vue-jest",},
};
组件测试实践
在src/components
下新建Parent.vue
和Child.vue
// front_test/src/components/Parent.vue
<template><div><span v-show="showSpan">Parent</span><child v-if="showChild"></child> // Child组件</div>
</template>
<script>
import Child from "./Child.vue";
export default {name: "Parent",components: {Child,},data() {return {showSpan: false,showChild: false,};},
};
</script>
// src/components/Child.vue
<template><div>Child</div>
</template>
<script>
export default {name: "Child",
};
</script>
👉我们要测的是,在Parent.vue中:
- 当
showSpan
为true
时,span
标签可以显示 Child
组件默认不会显示
编写的测试用例first_vue.spec.js
如下
// front_test/tests/unit/first_vue.spec.js
import { mount } from "@vue/test-utils";
import Parent from "@/components/Parent.vue";
import Child from "@/components/Child.vue";
describe("Parent", () => {it("does render a span", () => {const wrapper = mount(Parent, { // 挂载被测的组件data() {return {showSpan: true,};},});expect(wrapper.find("span").isVisible()).toBe(true); //find()用于查找元素,isVisible()用于测试v-show元素是否可见});it("does not render a Child component", () => {const wrapper = mount(Parent);expect(wrapper.findComponent(Child).exists()).toBe(false); //findComponent()用于查找组件,exist()用于测试v-if元素是否存在});
});
运行npm run test:unit first_vue.spec.js
,但是报了如下错误
> [vue-test-utils]: window is undefined, vue-test-utils needs to be run in a browser environment.
> You can run the tests in node using jsdom
这是jest
版本的问题,解决方法是降为v27,并在jest.config.js
中配置测试环境:
module.exports = {testEnvironment: "jsdom",
};
之后再运行npm run test first_vue.spec.js
就可以了🍻🍻🍻
端对端测试环境搭建与案例实践
安装Cypress
Cypress
的安装与Jest
一样,直接npm i cypress -D
"devDependencies": {..."cypress": "^10.9.0",...
},
安装Cypress后,有两种方式可以打开Cypress
- 直接在当前目录运行
npx cypress open
- 切到
node_modules/.bin
目录下运行cypress open
运行npx cypress open
,会出现Cypress的UI界面,同时在项目根目录下会自动生成cypress
文件夹和cypress.config.js
配置文件,可以看到默认的测试用例first_e2e.cy.js
通过
F:.
│--cypress
| │--e2e // 存放测试用例
| | first_e2e.cy.js
| │--fixtures // 存放静态资源
| | example.json
| │--support // 配置全局注入文件
| command.js
| e2e.js
│--src
// first_e2e.cy.js
describe("empty spec", () => {it("passes", () => {cy.visit("https://example.cypress.io");});
});
Cypress常用配置
- 启动命令
在package.json
中配置启动命令
"scripts": {"test:e2e": "cypress open"},
之后就可以使用npm run test:e2e
来打开Cypress了
- 项目文件夹
默认建立的cypress
文件夹是在根目录下,为了方便管理,在之前建立的tests
文件夹下新建e2e
文件夹,并将cypress
文件夹中的内容全部CV过去,同时在cypress.config.js
文件中添加如下配置:
// cypress.config.js
baseUrl: "http://localhost:8080", // 默认测试域名
fixturesFolder: "tests/e2e/fixtures", // 外部静态数据
screenshotsFolder: "tests/e2e/screenshots", // 屏幕快照
videosFolder: "tests/e2e/videos", // 录制视频
specPattern: "tests/e2e/specs/*.cy.{js,jsx,ts,tsx}", // 测试用例文件
supportFile: "tests/e2e/support/e2e.{js,jsx,ts,tsx}", // 配置全局注入
viewportHeight: 768, // 测试浏览器视口高度
viewportWidth: 1366, // 测试浏览器视口宽度
之后就可以删掉cypress文件夹了,由于对cypress.config.js
作了改动,所以需要重新启动Cypress来让配置生效
在这里事先将测试文件中的cy.visit("https://example.cypress.io")
改成了cy.("/")
,并运行npm run serve
将项目跑起来,这样就可以对我们的新建项目进行测试了
可以看到运行npm run test:e2e后
没任何问题🍻🍻🍻
E2E测试实践
下面我们就对打开的http://localhost:8080 页面进行测试。E2E测试旨在模拟用户真实的操作,确保应用在真实环境运行下一切正常工作,下面我们测试页面跳转的过程,测试内容有:
- 当前页面
h1
的内容为Welcome to Your Vue.js App
- 点击链接会跳转到Vue Cli官方文档,同时不打开新标签页
- 在Vue Cli官方文档的输入框中输入
@vue/cli-service
测试用例代码如下:
// tests/e2e/specs/first_e2e.cy.js
describe("tests vue2", () => {it("passes", () => {cy.visit("/"); // 访问默baseUrlcy.get("h1").should("contain", "Welcome to Your Vue.js App"); // 获取并断言h1内容cy.get('[data-test="vuecli"]').invoke("removeAttr", "target").click(); // 获取并点击vuecli链接cy.url().should("include", "cli.vuejs.org"); // 断言url内容cy.get("input").type("@vue/cli-service"); // 获取输入框并输入内容});
});
- 这里为vuecli链接添加了
data-test
属性以方便在测试时选择,详细可以参考Best Practices
// HelloWorld.vue
...<adata-test="vuecli"href="https://cli.vuejs.org"target="_blank"rel="noopener">vue-cli documentation</a>
...
- 另外,上述标签的
target
属性值为_blank
,所以会在新标签页打开链接,而Cypress由于其自动化测试的机制问题,是不支持多标签页测试的,因此在这里通过.invoke使用了jQuery
的removeAttr
方法,在点击前将target
属性去除,然后就可以在同一标签页打开新页面来进行测试 - 除了
cypress open
,我们也可以运行cypress run
命令,在不打开App的headless
模式下进行测试,同时Cypress
还会自动记录测试的整个过程,将视频和截图保存到配置的文件夹中,可自行在package.json中配置相关命令,这里我没配置,就直接在项目根路径下输入
npx cypress run --browser chrome // 指定运行环境为chrome浏览器
记录的测试过程如下
遇到的问题
- 安装
Cypress
后,运行npx cypress open
时报错:No version of Cypress is installed in: C:\Users\ChenH\AppData\Local\Cypress\Cache\10.9.0\Cypress Please reinstall Cypress by running: cypress install
👉这是由于缺少执行文件,有两种方法来解决:先从download.cypress.io/desktop.jso… 下载对应版本的安装包,这里是v10.9.0* 方法1:自行解压缩后,CV到C:\Users\ChenH\AppData\Local\Cypress\Cache\10.9.0\Cypress
下* 方法2:运行set CYPRESS_INSTALL_BINARY=d:\cypress.zip
设置环境变量为该压缩包的路径,然后重新运行npm i cypress -D
然后再运行npx cypress open
就可以打开Cypress了* 运行E2E测试案例时报错Cypress detected a cross origin error happened on page load: > Blocked a frame with origin "http://localhost:8080" from accessing a cross-origin frame.
👉这是“同源策略”导致的,按照提示在cypress.config.js
中配置chromeWebSecurity: false
就行参考资料
最后
最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。
有需要的小伙伴,可以点击下方卡片领取,无偿分享