不很久不很久以前

据说某家公司有两位前端,天天撸bug,为啥嘞?只怪测试MM倾人国,轻语哥哥有bug。✧(๑•̀ㅂ•́)و✧ 可是最近两位有点犯愁 Σ(っ °Д °;)っ。测试MM有几次提了紧急bug,都在旁边鼓励他们改bug了,可是线上bug重现排查比较麻烦,而且改了后还发现没改好,惹得测试MM潸然泪下,好生埋汰。怎么办呢?

前端君666某天发现了E2E测试神器cypress后,暗中偷练神功,改bug越来越6,测试MM每天笑着对他说,666你真6,MM好喜欢呀(๑•́ ₃ •̀๑) 另一位前端君555每天面对堆积如山的bug长吁短叹,测试MM提完新bug后都不理他了≡ ̄﹏ ̄≡

作为一个追求代码永无bug、顺带跟测试MM沟通产品的有理想的前端 (ง •̀_•́)ง,我觉得有必要学习一下怎么使用cypress来进行E2E测试,以此来提高代码质量。那么我们来看看怎么入门cypress测试框架。

cypress三问 - 你是谁

cypress是在mocha式API基础上构建的一套开箱可用的E2E测试框架,对比其他测试框架,它提供一套自己的最佳实践方案,无需其他测试工具库,配置方便简单但功能异常强大,可以使用webpack项目配置,还提供了一个强大的GUI图形工具。入门简单,上手方便,怎么舒服怎么来呀 (。→‿←。)

cypressGUI方式的测试使用真实浏览器,非GUI方式使用chrome-headless,不是用模拟方式进行测试,更真实的展现实际环境中的测试过程和结果。

cypress三问 - 你有啥优势

cypress有几大自带的强大功能:

  • 自带GUI工具,想测啥就点啥,还可以查看整个测试过程,想录屏还可以录屏哟(录屏可以发给测试MM看,保准她说哥哥真厉害哟。 一般人我不告诉他๑乛◡乛๑)
  • 测试的每一步都有snapshot,可以通过GUI工具查看每个过程的页面状态,不是截图而是真是的页面DOM环境哟!
  • 自带数据mock和请求拦截机制,还原线上数据引起的bug别提有多轻松了
  • 和wepbakc配置,实现无论修改测试文件还是被测试代码都可以自动重测
    • 小Tips:可以给测试用例加上only或者skip来避免重测测试文件里的所有用例: it.only('只测试这个哟); it.skip('不要测这个');

cypress三问 - 怎么用

安装

  • yarn add cypress 或者 npm install cypress
  • 安装完毕后,./node_modules/.bin/cypress install安装cypress环境(包括GUI工具)

配置

  • package.json: 配置GUI和非GUI(terminal)两种方式来运行cypress
    "scripts": {
"cypress": "cypress run",
"cypress-gui": "cypress open",

⚠️ 配置好后 先运行 yarn cypress[-gui] 或者 npm run cypress[-gui](中括号意思是可选)来初始化cypress生成默认配置和目录

  • cypress.json(与package.json同级目录): cypress提供比较灵活的配置,可以根据自己需要定制行为,以下列一下我对一个项目的配置
{
"baseUrl": "http://localhost:8080", // 本地开发服务地址(webpack-dev-server)
"integrationFolder": "src", // 自定义"src"为测试文件根目录,默认是"cypress/integration"
"testFiles": "**/*.cypress.spec.js", // 自定义测试文件的匹配正则,默认是"**/*.*",即所有文件
"videoRecording": false, // 关闭录屏功能, 如果开启录屏功能,记得将"cypress/screenshots"目录加入".gitignore",防止不小心将录屏加到git中
"viewportHeight": 800, // 设置测试环境的页面视图的高度
"viewportWidth": 1600 // 设置测试环境的页面视图的宽度
}
  • cypress/plugins/index.js: cypress运行环境配置,可以用来配置webpack等。以下是配置webpack别名范例。默认这里不需要配置。
// 参考官方例子地址 https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/preprocessors__typescript-webpack/cypress/plugins/index.js
const wp = require("@cypress/webpack-preprocessor");
const path = require('path'); function resolve(dir) {
return path.join(__dirname, "../..", dir);
} module.exports = on => {
const options = {
webpackOptions: {
resolve: {
alias: {
"@": resolve("src"),
cypress: resolve("cypress")
}
}
}
};
on("file:preprocessor", wp(options));
};

万事俱备,测测测

  • 简单的一个例子
describe('测试页面包含某元素', () => {
it('有云 "前端哥哥们真帅,前端妹妹们真漂亮"', () => {
cy.contains("前端哥哥们真帅,前端妹妹们真漂亮");
}); it('要有一个链接', () => {
cy.get('a').should('have.length', 1);
}); it('不存在class含有abc的元素', () => {
cy.get('.abc').should('have.length', 0);
});
});
  • 互动的例子
describe('一起动', () => {
it('获取输入框,输入文字并按enter键', () => {
const text = 'not exist';
// type api用法: https://docs.cypress.io/api/commands/type.html#Usage
cy.get('input').type(`${text}{enter}`);
}); it('点击按钮', () => {
cy.get('button').click();
});
});
  • 网络请求mock例子

Tip1: cy.route的路径匹配是严格的,所以要注意是否需要加通配符。如 cy.route('/api/search', [])不会拦截/api/search?keyword=abc,只会拦截/api/search

Tip2: cy.route的method要注意,默认是GETcy.route('/api/posts')cy.route('POST', '/api/posts') 是不一样的。

describe('要啥给啥', () => {
beforeEach(() => {
cy.server(); // 一定要在 cy.route 前调用
cy
.fixture('/posts/list.json') // 我们在 cypress/fixtures 内创建mock用的数据
.as('postsData'); // 给 mock 数据取别名,以后 cy.route 使用
cy
.route('/api/posts', '@postsData')
.as('getPostsRoute'); // 给请求取别名,以供 cy.wait 使用
}) it('进入列表页,拦截列表请求接口', () => {
cy.wait('@getPostsRoute'); // 等待被拦截的接口请求完成 cy.get('.post').should('have.length', 10); // 要有10条数据被渲染到页面上
});
})
  • 实际场景例子: 结合上面所有姿势,我们现在测试搜索页面的搜索、操作结果
describe('test search page', () => {
// 几个 route 路径变量
const searchRoutePath = '/api/items/activities?query=*';
const deleteActivityRoutePath = '/api/activities/*/items/batch?num_iids[]=*';
const undoActivityRoutePath = '/api/activities/*/items/undo'; function search(keyword) {
// 将搜索行为和等待搜索返回封装起来
cy
.fixture('items/activities.json')
// 处理mock数据,只返回符合搜索结构的数据
.then(data => data.filter(item => item.title.indexOf(keyword) !== -1))
.as('searchResult');
cy.server();
cy.route(searchRoutePath, '@searchResult').as('searchRoute'); const input = cy.get('input');
input.clear(); // 清空输入框内文本 input.type(`${keyword}{enter}`); cy.wait('@searchRoute');
} before(() => {
// 进行所有测试前,先访问搜索页
cy.visit('/activities/search');
}); it('should show no data tip when search result is empty', () => {
const text = 'not exist';
search(text);
cy.contains(`没有找到关于 ${text} 的结果`);
}); it('should remove activity from list when clean successful', () => {
search('成功'); cy
.route('delete', deleteActivityRoutePath, {
success: 0,
fail: 0,
waiting: 0,
})
.as('deleteActivityResponse'); // within是让cy执行的context保持在'.activities-search'这个dom节点内
// 默认cy的执行是以上一个cy命令结果作为context
// 如 "cy.get('a'); cy.get('span')",cy会在上一个命令找到的'a'标签中查找'span'
cy.get('.activities-search').within(() => {
const items = cy.get('.result-item');
items.should('have.length', 1);
const applyList = items.get('.apply-list'); applyList.should('not.be.visible'); // 每个数据项内详细内容区域是隐藏的 const toggleBtn = items.get('.item-apply-count');
toggleBtn.click(); // 点击显示详细内容区
applyList.should('be.visible');
applyList.children().should('have.length', 1); // 详细内容区内数据只有1条 const cleanBtn = cy.contains('退出');
cleanBtn.click(); // 点击详细内容区里的“退出”按钮 cy.wait('@deleteActivityResponse'); // 等待“退出”请求返回
cy.get('.apply-list').should('be', null); // 退出成功后,详细内容区数据减1,即空
});
});
});

几个必读文档

关于测试覆盖率

目前cypress没有内置测试覆盖率统计功能,github上有专门的issue在跟踪这个,后续应该会有。issue上也有几个临时方案,目前我倾向使用chrome自带的来查看。在GUI打开的测试的浏览器中打开devtools,切到Sources, 按下cmd+shift+p(windows用户按ctrl+shift+p),输入coverage,选择重新刷新并统计代码执行覆盖率。


那么,high起来

为了高(撩)质(测)量(试)代(M)码(M),high起来。喜欢前端MM的可以手把手教起来了 (¬_¬)


本文章首发于本人公众号:枫之叶。有兴趣的可以长按下方二维码关注。^v^


撩测试MM神器cypress使用入门的更多相关文章

  1. 推荐一个linux下的web压力测试工具神器webbench

    推荐一个linux下的web压力测试工具神器webbench2014-04-30 09:35:29   来源:   评论:0 点击:880 用多了apache的ab工具之后你就会发现ab存在很多问题, ...

  2. 万能js实现翻页,动态生成内容自动翻页,兼容各种浏览器(已测试)----神器版!

    转--http://www.2cto.com/kf/201402/277535.html 万能js实现翻页,动态生成内容自动翻页,兼容各种浏览器(已测试)----神器版! 2014-02-11     ...

  3. 推荐一款全能测试开发神器:Mockoon!1分钟快速上手!

    1. 说一下背景 在日常开发或者测试工作中,经常会因为下游服务不可用或者不稳定时,通过工具或者技术手段去模拟一个HTTP Server,或者模拟所需要的接口数据. 这个时候,很多人脑海里,都会想到可以 ...

  4. 一键自动生成 java junit 测试代码神器 gen-test-plugin 入门介绍

    gen-test-plugin 我们日常编写代码的过程中,经常需要为代码编写测试案例. 随着对代码质量的要求越来越高,很多公司开始通过代码的测试覆盖率作为 QA 的一个评定指标. 本框架可以一键生成所 ...

  5. 安全性测试:OWASP ZAP使用入门指南

    免责声明: 本文意在讨论使用工具来应对软件研发领域中,日益增长的安全性质量测试需求.本文涉及到的工具不可被用于攻击目的. 1. 安全性测试 前些天,一则12306用户账号泄露的新闻迅速发酵,引起了购票 ...

  6. e2e测试框架之Cypress

    谈起web自动化测试,大家首先想到的是Selenium!随着近几年前端技术的发展,出现了不少前端测试框架,这些测试框架大多并不依赖于Selenium,这一点跟后端测试框架有很大不同,如Robot Fr ...

  7. Web压力测试工具 LoadRunner12.x简易入门教程--(一)回放与录制

        LoadRunner12.x简易入门教程--(一)回放与录制 今天在这里分享一下LoadRunner12.x版本的入门使用方法,希望对刚接触LoadRunner的童鞋有所帮助. LoadRun ...

  8. 前端开发神器Charles从入门到卸载

    前言 本文将带大家学习使用前端开发神器-charles,从基本的下载安装到常见配置使用,为大家一一讲解. 一.花式夸奖Charles 截取 Http 和 Https 网络封包. 支持重发网络请求,方便 ...

  9. 学习版pytest内核测试平台开发万字长文入门篇

    前言 2021年,测试平台如雨后春笋般冒了出来,我就是其中一员,写了一款pytest内核测试平台,在公司落地.分享出来后,有同学觉得挺不错,希望能开源,本着"公司代码不要传到网上去,以免引起 ...

随机推荐

  1. wpf 模板绑定父对象

    有两种方式可以实现在模板中元素绑定到父对象 1.<ContentPresenter Margin=”{TemplateBinding Padding}”/> 2.Color=”{Bindi ...

  2. python爬虫下正则各种字符串数据匹配

    s = '*\/:?"<>|' #这9个字符在Windows系统下是不可以出现在文件名中的str1 = '\巴拉<1"!11[]>1*hgn/p:?|' # ...

  3. conda查找安装包的版本以及安装特定版本的包

    如下图 想要安装特定版本的torchvision,然后conda search torchvision,能够列出conda云上所有的安装包 然后,安装包的时候,conda install 包名=版本就 ...

  4. canvas签名

    <template> <div class="share" style="background:#fff"> <div class ...

  5. 第十章、sys模块

    目录 第十章.sys模块 第十章.sys模块 方法 详解 sys.argv 命令行参数List,第一个元素是程序本身路径 sys.modules.keys() 返回所有已经导入的模块列表 sys.ex ...

  6. deep_learning_neural network梯度下降

    神经网络优化算法:梯度下降法.Momentum.RMSprop和Adam 最近回顾神经网络的知识,简单做一些整理,归档一下神经网络优化算法的知识.关于神经网络的优化,吴恩达的深度学习课程讲解得非常通俗 ...

  7. String类型为什么不可变

    在学习Java的过程中,我们会被告知 String 被设计成不可变的类型.为什么 String 会被 Java 开发者有如此特殊的对待?他们的设计意图和设计理念到底是什么?因此,我带着以下三个问题,对 ...

  8. Beta冲刺版本第二天

    该作业所属课程:https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass2 作业要求地址:https://edu.cnblogs.com ...

  9. Windows下Redis如何永久更改密码

    公司使用的是Spring-session-redis 需要给Redis配置一个密码 本来我配置密码的方法是 先打开Redis服务 在采用 命令 CONFIG SET requirepass " ...

  10. 2019.8.30 记录一个Swiper的使用

    导入     flutter_swiper: ^1.1.6 引入     import 'package:flutter_screenutil/flutter_screenutil.dart'; 已下 ...