本文主要首先主要介绍了什么是自动化测试,接着对常用的自动化测试框架进行了对比分析,最后,介绍了如果将自动化测试框架Cypress运用在项目中。
一、自动化测试概述
为了保障软件质量,并减少重复性的测试工作,自动化测试已经被广泛运用。在开始学习自动化测试之前,我们很有必要先搞清楚这几个问题,什么是自动化测试?为什么要做自动化测试?哪些项目适合做自动化测试?
1、什么是自动化测试
自动化测试是一种测试方法,是指使用特定的软件,去控制测试流程,并比较实际结果与预期结果之间的差异。通过将测试自动化,可以把人对软件的测试行为转化为由机器自动执行测试的行为,从而替代大量的手工测试操作,使得测试可以快速,反复的进行。
关于自动化测试,有一个测试金字塔模型,该模型把测试从下到上分为了单元测试、集成测试和端到端测试(E2E测试/UI界面测试)。越往金字塔底层,测试成本越低,效率也越高,而越往金字塔的顶层,测试成本会逐渐增高,收益也会越低。
- 单元测试
单元测试又称为模块测试,主要针对程序中最小可测试单元(一般指方法,类)的测试,具备投入小、收益产出高的特征,可以较早期地发现代码缺陷,适用于公共函数库的测试。
- 集成测试
集成测试主要包括模块接口测试,子功能模块集成起来的功能模块测试等,目的是为了验证在单元测试的基础上,所有模块集成起来的子系统、子功能是否仍然满足质量目标。
- 端到端测试
端到端测试的主要目的是从软件使用者角度来检验软件的质量,如打开浏览器,进行一系列的操作,验证界面或功能是否符合预期。
不同类型的项目,具有不同的测试场景,因此也需要采用不同的测试类型。对于开发人员来说,单元测试专注于代码底层,可能是一种比较友好的选择。但是站在产品的角度上,也许端到端测试(E2E)是更好的选择,更能保障产品功能符合预期。
讲完了自动化测试类型,我们再来看看测试中常用的测试模式,一般常用的测试模式包括TDD和BDD两种。
- TDD
TDD(测试驱动开发,Test Driven Development),TDD是指先写测试用例代码,再写功能代码,并且不断的重复上述步骤直到完成开发工作。TDD一般结合单元测试使用,是白盒测试。
- BDD
BDD(行为驱动开发,Behavior Driven Development),BDD是指先写功能代码,再写测试用例代码,BDD一般结合集成测试或端到端测试使用,是黑盒测试。
当然,是选择TDD还是BDD,也是需要从项目的实际角度出发考虑,再做选择。
2、为什么要做自动化测试
接下来,我们再来聊聊为什么要做自动化测试?在实际的项目开发中,我们常常会遇到以下问题:
- 产品迭代频繁
迭代过程中不可避免的需要新增功能或修改功能,怎么保障新功能的发布不会影响原有功能呢?
- 多人共同参与开发,代码维护难
项目开发过程中多人参与开发,人员变动频繁,开发过程中可能出现误删或误改他人代码逻辑的问题,如何保障代码的质量和可靠性?
- 测试人力不足,回归测试耗时耗力
为了解决上面提到的两个问题,其实方法很简单,就是每次新功能发布后,都对原有功能再进行回归测试。但是又可能遇到测试人力不足的情况,自己手动进行回归测试又耗时耗力,如何才能减少重复性工作,提高效率呢?说到这里,自动化测试就派上用场啦~
那项目引入自动化测试有什么好处呢?自动化测试的好处主要包括了以下几点。
- 验证代码正确性,保障产品质量
可以验证代码或产品功能的正确性,确保每次产品迭代,新功能和原有功能能够正确集成,保证产品质量。
- 提高测试效率
编写的测试用例具有一次编写,多次运行的特点,通过执行测试脚本,可以实现使得测试快速,反复的进行,可以替代大量的重复性手动测试工作,提高效率。
- 起到文档作用
编写的测试用例可以起到文档的作用,有利于项目后续的维护。
3、哪些项目适合引入自动化测试
既然自动化测试有这么多好处,那是不是所有项目都适合引入自动化测试呢?当然不是!自动化测试需要进行测试用例的编写,需要一定的开发成本,我们需要立足于项目本身,再来决定是否适合引入自动化测试。
- 适合引入自动化测试的项目
1)产品周期较长,需要不断进行迭代/重构的项目。2)公共库类的开发维护。
- 不适合引入自动化测试的项目
1)产品周期过短的项目。2)需求变动过于频繁的项目。
二、前端自动化测试框架选择
在明确了我们的项目有必要引入自动化测试之后,就需要选择一款自动化测试框架或工具来帮助我们完成自动化测试工作啦~
1、测试框架对比
下面主要对比了现在常用的Web前端自动化测试框架,如果需要了解更多的框架,可以参考测试框架选型
Jest | Mocha | Selenium/WebDriver | Nightwatch | TestCafe | Cypress | |
支持测试类型 | 单元测试 | 单元测试 | E2E测试 | E2E测试、Node.js单元和集成测试 | E2E测试 | 单元测试、集成测试、E2E测试 |
支持语言 | JS、Node | JS、Node | Java、Python、C#、JS | JS、Node | JS、TypeScript | JS |
是否支持可视化测试 | 否 | 否 | 否 | 否 | 否 | 是 |
是否自带断言库 | 是 | 否 | 否 | 是 | 否 | 是 |
是否开箱即用 | 是 | 否 | 否 | 是(安装配置繁琐) | 是 | 是 |
录制生成脚本 | / | / | 支持 | 不支持 | 不支持 | 不支持 |
Github Star | 35.2k | 20.5k | 20.8k | 10.7k | 8.9k | 31.3k |
文档 | Jest文档 | Mocha文档 | Selenium文档 | Nightwatch文档 | Testcafe文档 | Cypress文档 |
在上述框架中,由于Cypress能够同时支持单元测试、集成测试和E2E测试,提供了一套完成的测试解决方案,能够满足我们的需求。此外,Cypress支持JS编写测试用例,支持Jquery元素定位选择器,支持Headless和CI持续集成,运行速度快,上手成本低,并且具有可视化调试界面,方便定位问题。因此决定尝试将Cypress运用到项目中。
三、Cypress实践
接下来,主要介绍如何将Cypress运用在项目中。
1、Cypress安装
在安装Cypress时,可以直接在原有的项目上进行安装,也可以另起一个项目安装。
npm install cypress --save-dev
2、Cypress启动
Cypress主要包含以下两种启动方式:
1)命令行执行npx cypress open:会在浏览器打开测试用例集的界面,需要手动运行。
2)命令行执行npx cypress run:会以无头浏览器模式运行指定的所有测试用例,没有可视化界面,但运行过程中会录制整个测试过程的视频,可在cypress/videos目录下查看。
当然,除了直接在命令行运行上述命令,也可以通过配置package.json的scripts字段来定义启动方式。
"scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint", "cypress:open": "npx cypress open", "cypress:run": "npx cypress run" }
- 可视化界面运行
如果我们需要在可视化界面进行测试,在配置好package.json后,只需要执行npm run cypress:open,就可以启动Cypress,实现可视化调试,如果在启动的过程中遇到以下错误,可以先执行npx cypress install -force,再重新启动Cypress。
如果成功启动Cypress,将会看到以下界面,examples目录下是cypress自带的测试用例演示代码(如果后面不需要,我们可以将这些测试用例删除),点击其中的某个测试用例,将会自动打开浏览器运行测试用例。
如果我们是第一次启动Cypress,会发现在项目根目录下也自动生成了cypress.json配置文件和cypress目录。其中,integration文件夹就是我们用来存放测试用例的目录,可以在cypress.json中自定义这些默认目录的命名。
-
无头浏览器模式运行
如果我们想以无头浏览器模式运行,在配置好package.json后,需要执行npm run cypress:run,Cypress就会以无头浏览器的模式运行指定的所有测试用例。
3、编写测试用例
接下来以验证百度页面的搜索功能为例,演示如何编写测试用例,测试用例可以以.spec.js或.js结尾命名,并放入cypress/integration中。
项目目录如下所示,在cypress/integration中创建test.js或test.spec.js测试用例文件。
接着,可以在test.js中开始编写测试用例,Cypress支持Jquery元素选择器及汉字选择器,并且也支持链式操作,此外,由于Cypress拥有自动等待机制,我们无须在测试中添加wait或sleep,Cypress会自动等待元素至可操作状态时才执行命令或断言。
/// <reference types="cypress" /> context(‘百度页面测试‘, () => { it(‘访问百度页面,验证搜索功能‘, () => { cy.visit(‘https://www.baidu.com‘).then(() => { // 1. 输入搜索内容 cy.get(".s_ipt").should("exist").type("Cypress自动化测试"); // 2. 点击百度一下按钮 cy.get(".s_btn").contains("百度一下").should("exist").click(); // 3. 验证搜索内容不为空 cy.get("#content_left").find("div").then(ele => { expect(ele.length).gt(0); }); }); }); });
编写完上述代码,我们就可以直接启动Cypress运行啦,当然,我们也可以根据实际需要在cypress.json进行一些配置,下面给出了一些常用的配置,可以在Cypress文档查看更多配置。
最后,执行npm run cypress:open启动Cypress,启动成功后,我们就可以看到以下界面,点击test.js,就会在浏览器中运行该测试用例。
在测试用例执行的过程中,每一步操作都会被记录下来,可以点击左边的界面对每一步的操作进行回看,可以帮助我们快速定位问题。
4、Cypess文件上传/下载
在实际的使用过程中,我们通常也需要验证文件上传或下载功能,而Cypress也能够满足这些需求。
1)文件上传
首先需要安装cypress-upload-file插件。
npm install cypress-upload-file --save-dev
将要上传的文件放到cypress/fixtures中。
编写测试用例代码。
/// <reference types="cypress" /> context(‘文件上传‘, () => { it(‘验证文件上传功能‘, () => { // 访问页面,此处步骤省略 let file = "file/cover.jpg"; cy.get("input[type=‘file‘]").attachFile(file); // 执行断言,此处步骤省略 }); });
2)文件下载
若我们在运行测试用例的过程中存在文件下载操作,Cypress会自动在cypress目录下创建一个downloads目录,所下载的文件会自动保存在该目录中。
可以在测试用例中读取并解析下载的文件。
/// <reference types="cypress" /> const path = require("path"); context(‘文件下载‘, () => { it(‘验证文件下载功能‘, () => { // 访问页面,执行下载操作,此处步骤省略 const downloadsFolder = Cypress.config("downloadsFolder"); const downloadedFilename = path.join(downloadsFolder, "下载文件.xls"); // 读取文件 cy.readFile(downloadedFilename).then(data => { // 执行断言,此处步骤省略 }); }); });
5、Cypress测试报告
在执行完自动化测试后,我们通常都希望能够得到一份详细的测试报告,而Cypress也能够提供这个功能。Cypress除了内置的测试报告,也支持用户自定义报告格式。
1)内置的测试报告
Cypress内置的测试报告主要包括了spec格式报告(在控制台窗口输出嵌套分级视图),json格式报告(在控制台窗口输出一个大的json对象)和junit格式报告(输出一个xml文件)。以spec格式报告为例,在启动cypress时加上以下参数即可。
2)自定义的测试报告
常用的自定义测试报告有Mochawesome报告,Mochawesome是与Mocha一起使用的自定义报告程序,并与mochawesome-report-generator结合使用以生成独立的HTML/CSS报告。
安装mocha和mochawesome。
npm install mocha --save-dev
npm install mochawesome --save-dev
修改启动参数。
运行npm run cypress:run,执行结束后,会在项目根目录下生成mochawesome-report目录。
在浏览器中打开mochawesome.html,就可以查看可视化测试报告。
四、编写可维护的测试脚本
在实际编写测试用例的过程中,随着页面的增多,我们常常会遇到以下这些问题,而这个时候,如何编写可维护的测试脚本,方便后期维护,也显得非常重要,这里也总结了实际开发中的一些经验。
1、测试用例代码结构组织
在编写测试用例时,我们可以一个页面对应一个测试文件,也可以同个功能模块的页面一起对应一个测试文件,并且和平时开发中所采用的代码组织结构类似,将不同的测试文件划分到对应的目录下进行管理,方便后期的维护。
2、页面选择器统一管理
在E2E测试中,我们通常需要获取页面元素,才能够进行点击等操作。而Cypress支持Jquery选择器,我们可以通过元素的class或id定位元素。但是一旦页面的类名或id发生变化,我们不得不修改对应页面的所有测试用例。
在编写测试用例的过程中,我们可以将页面选择器进行统一管理,实现类名或id选择器和逻辑代码的分离。对于每个页面或者每个测试文件,可以创建一个对应的xxxControl.js文件,在该文件中,将会定义一个json对象并且export出来,其中,key为我们自己定义的选择器名称,而value值对应页面中实际的class或id。
由于目前项目中使用到了iview组件库,因此也提取出了commonControl.js,对iview的选择器进行统一管理。
由于每个页面都采用到了iview组件,因此每个页面或每个测试文件对应的control.js都需要将上面的commonControl.js引入进来。
最后,每个测试文件只需要引入对应的control.js,就可以通过自己定义的key值获取页面真正的class或id。
上面的方法看起来虽然麻烦了点,但是有两个好处,首先,采用自己定义的key值,更容易方便我们记忆,可以减少编写测试用例过程中反复查看页面元素对应的class或id名。其次,当页面的类名或id发生变化时,我们只需要修改页面对应的control.js文件就可以,而不用修改所有的测试用例文件,有利于后续的维护。
3、路径名及接口统一管理
在编写测试用例的过程中,我们通常需要使用cy.visit()去访问某个页面,或者使用cy.request()去调用后台接口以请求数据或创建测试数据,对于页面url或后台接口api,我们也可以放入某个文件中进行统一管理。
4、代码复用
在测试的过程中,我们可能会注意到,不同的页面可能会存在一些相同的功能。比如像目前的项目中,不同的页面都需要对一些操作进行弹框确认或表单输入,而在验证弹框功能是否正确的过程中,我们都需要对弹框执行点击确定按钮、点击取消按钮、点击关闭按钮等操作,而这个时候就可以采用面向对象编程的方法实现代码的封装和复用。
定义一个弹框类,并且定义属性和方法。
在测试文件中,需要实例化对象,并调用相关的方法完成某个操作。
当然,有些方法并不是所有的页面都共有的,但是在某个页面或功能中会反复使用到,因此也可以为每个页面或每个测试文件单独封装相应的方法。比如为课程管理页面封装相应的通用方法。
以上就是关于将Cypress运用在项目中的一些总结,而如何将Cypress和CI/CD结合,并且实现自动化测试的定时执行,也是接下来需要继续完成的内容~