撩测试MM神器cypress使用入门

匿名 (未验证) 提交于 2019-12-03 00:17:01

不很久不很久以前

前端君666某天发现了E2E测试神器cypress

作为一个追求代码永无bugcypress来进行E2E测试,以此来提高代码质量。那么我们来看看怎么入门cypress测试框架。

cypress三问 - 你是谁

cypress是在mocha式API基础上构建的一套开箱可用的E2E测试框架,对比其他测试框架,它提供一套自己的最佳实践方案,无需其他测试工具库,配置方便简单但功能异常强大,可以使用webpack项目配置,还提供了一个强大的GUI

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

cypress三问 - 你有啥优势

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

  • 测试的每一步都有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起来


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!