乐趣区

关于cypress:Cypress-框架的介绍

Cypress 简介

  • 基于 JavaScript 的前端测试工具,能够对浏览器中运行的任何内容进行疾速、简略、牢靠的测试
  • Cypress 是 自集成 的,提供了一套残缺的端到端测试,毋庸借助其余内部工具,装置后即可疾速地创立、编写、运行测试用例,且对每一步操作都反对回看
  • 不同于其余只能测试 UI 层的前端测试工具,Cypress 容许编写所有类型的测试,笼罩了测试金字塔模型的所有测试类型【界面测试,集成测试,单元测试】
  • Cypress 底层协定 不采纳 WebDriver

Cypress 原理

Webdriver 运行的形式

  • 大多数测试工具(如:Selenium/webdriver)通过在 内部浏览器运行 并在网络上执行近程命令来运行
  • 因为 Webdriver 底层通信协议基于 JSON Wire Protocol,运行须要网络通信

Cypress 运行的形式

Cypress 和 Webdriver 形式齐全相同,它与应用程序在雷同的生命周期里执行

Cypress 运行测试的大抵流程

运行测试后,Cypress 应用 webpack 将测试代码中的 所有模块 bundle 到一个 js 文件中
而后,运行浏览器,并且将测试代码注入到一个空白页中,而后它将在浏览器中运行测试代码【能够了解成:Cypress 将测试代码放到一个 iframe 中运行】

Cypress 运行测试的技术流程

  1. 每次测试首次加载 Cypress 时,外部 Cypress Web 应用程序先把本人 托管 在本地的一个随机端口上【如:http://localhost:65874】
  2. 在辨认出测试中收回的第一个 cy.visit() 命令后,Cypress 会更改本地 URL 以匹配你近程应用程序的 Origin【满足同源策略】,这使得你的 测试代码和应用程序 能够在 同一个 Run Loop 中运行

Cypress 运行更快的根本原因

  • Cypress 测试代码和应用程序均运行在由 Cypress 全权管制的浏览器中
  • 且它们运行 在同一个 Domain 下的不同 iframe 中 ,所以 Cypress 的测试代码能够间接操作 DOM、Window Objects、Local Storages 而 毋庸通过网络拜访

Cypress 稳定性、可靠性更高的起因

  • Cypress 还能够在网络层进行 即时读取和更改网络流量 的操作
  • Cypress 背地是 Node.js Process 管制的 Proxy 进行转发,这使得 Cypress 不仅能够批改进出浏览器的所有内容,还能够更改可能影响自动化操作的代码
  • Cypress 绝对于其余测试工具来说,能从根本上 管制整个自动化测试的流程

Cypress 架构图

Cypress 的个性

工夫穿梭【历史记录】

  • Cypress 在测试代码 运行时会主动拍照
  • 等测试运行完结后,用户可在 Cypress 提供的 Test Runner 里,通过悬停在命令上的形式查看运行时每一步都产生了什么

实时从新加载

当测试代码批改保留后,Cypress 会主动加载改变中央,并从新运行测试

Spies(特务)、Stubs(存根)、Clock(时钟)

  • Cypress 容许你验证并管制函数行为,Mock 服务器的响应,更改零碎工夫
  • 单元测试触手可及!

运行后果一致性

Cypress 架构不应用 Selenium 或 Webdriver,在运行速度、可靠性测试、测试后果一致性上均有良好保障

可调试性

当测试失败时,能够间接从开发者工具(F12 Chrome DevTools)进行调试,这相熟吧??

主动期待

  • 应用 Cypress,永远毋庸在测试中增加 强制期待、隐性期待、显性期待
  • Cypress 会主动期待元素至牢靠操作状态时才执行命令或断言
  • 异步操作触手可及!

网络流量管制

Cypress 能够 Mock 服务器返回的后果,毋庸依赖后端服务器,即可实现模仿网络申请

截图和视频

Cypress 在测试运行失败时会主动截图,在无头运行时(无 GUI 界面)会录制整个测试套件的视频

Cypress 劣势的总结

归纳起来,具体长处如下:

  1. 自集成大部分测试所需的库
    像咱们在用 Selenium 时,须要集成单元测试框架(unittest、pytest),想要难看的测试报告还得集成(allure),想要 Mock 还得引入对应的 Mock 库而 Cypress 是开箱即用!啥意思?看下图!

  1. 执行速度快,不便调试。
  2. 实用于中小我的项目,对立技术栈的团队。
  3. 自集成测试截图,很不便。

Cypress 毛病

  1. 学习老本过于高,cypress 的框架注定只能应用 js,且对测试来说 js 的性价比没有 python 高,selenium 还反对各种语言。
  2. 坑坑洼洼太多,cypress 在最新的教程都在 2020 年 8 月才出,一些莫名的 bug 和潜在的知识点都无从通晓。
  3. cypress 应用的是异步调用,不太懂的在这个坑里爬都爬不出。
  4. cypress 最佳的使用者其实是前端,但个别前端不干这个。
  5. 灵活性没 selenium 高,selenium 有太多的库反对其实现骚操作。
  6. 对数据的解决没有 python 好用,js 在一些数据处理上会有莫名的问题,不太懂前端就有点难堪。

默认文件构造

在应用 cypress open 命令首次关上 Cypress,Cypress 会主动进行初始化配置并 生成一个默认的文件夹构造,如下图

fixtures 测试夹具

简介

  • 测试夹具通常配合 cy.fixture() 应用
  • 次要用来 存储 测试用例的 内部静态数据
  • fixtures 默认就在 cypress/fixtures 目录下,但也能够配置到另一个目录

内部静态数据的详解

  • 测试夹具的静态数据通常存储在 .json 文件中,如主动生成的 examples.json
  • 静态数据通常是某个 网络申请对应的响应局部,包含 HTTP 状态码和返回值,个别是复制过去更改而不是本人手工填写

fixtures 的理论利用场景

如果你的测试须要对某些 内部接口 进行拜访并 依赖它的返回值,则能够应用测试夹具而无须真正拜访这个接口(有点相似 mock)

应用测试夹具的益处

  • 打消了对外部功能模块的依赖
  • 已编写的测试用例能够应用测试夹具 提供的固定返回值,并且你确切晓得这个返回值是你想要的
  • 因为毋庸真正地发送网络申请,所以测试更快

命令示例

要查看 Cypress 中每个命令的示例,能够关上 cypress/integration/examples,外面都是官网提供的栗子

test file 测试文件

简介

测试文件就是 测试用例,默认位于 cypress/integration ,但也能够配置到另一个目录

测试文件格式

  • 所有在 integration 文件下,且文件格式是以下的文件都将被 Cypress 辨认为测试文件
  • .js:一般的 JavaScript 编写的文件【最罕用啦】
  • .jsx:带有扩大的 JavaScript 文件,其中能够蕴含解决 XML 的 ECMAScript
  • .coffee:一套 JavaScript 转译的语言。有更严格的语法
  • .cjsx:CoffeeScript 中的 jsx 文件

创立好后,Cypress 的 Test Runner 刷新之后就能够看到对应测试文件了

plugin file 插件文件

前言

  • Cypress 独有长处就是 测试代码运行在浏览器之内,使得 Cypress 跟其余的测试框架相比,有显著的架构劣势
  • 这长处尽管提供了可靠性测试,但也使得和在浏览器之外进行通信更加艰难【痛点:和内部通信艰难】

插件文件的诞生

  • Cypress 为了解决上述痛点提供了一些现成的插件,使你能够 批改或扩大 Cypress 的外部行为(如:动静批改配置信息和环境变量等),也能够自定义本人的插件
  • 默认状况,插件位于 cypress/plugins/index.js 中,但能够配置到另一个目录
  • 为了不便,每个 测试文件运行之前 ,Cypress 都会 主动加载插件文件 cypress/plugins/index.js

插件的利用场景

  • 动静更改来自 cypress.json,cypress.env.json,CLI 或零碎环境变量的 已解析配置和环境变量
  • 批改特定浏览器的启动参数
  • 将音讯间接从测试代码传递到后端

support file 反对文件

简介

  • 反对文件目录是搁置可重用配置项,如 底层通用函数或全局默认配置
  • 反对文件默认位于 cypress/support/index.js 中,但能够配置到另一个目录
  • 为了不便,每个 测试文件运行之前 ,Cypress 都会 主动加载反对文件 cypress/support/index.js

如何应用反对文件加载钱包

只须要在 cypress/support/commands.ts 文件里配置即可,原生的文件是commands.js,当初将其后缀改为 ts。

还配置了一个 commands.d.ts。相干代码如下

commands.js中,须要应用三个库:JsonRpcProvider、Wallet、Eip1193Bridge,还须要在 index.js 中导入commands,如下:

Commands的命令有 3 个,
Cypress.Commands.add(name, callbackFn)、
Cypress.Commands.add(name, options, callbackFn)、
Cypress.Commands.overwrite(name, callbackFn)

参数阐明

  • name:要增加或笼罩的命令的名称
  • callbackFn:自定义命令的回调函数,回调函数里自定义函数所需实现的操作步骤
  • options:容许自定义命令的隐性行为

如下,在 visit 函数中注入钱包,因为每次执行用例都会应用 visit 进入网址。测试用例如下:

commands.ts文件其余代码

// ***********************************************
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************

import {JsonRpcProvider} from '@ethersproject/providers'
import {Wallet} from '@ethersproject/wallet'
import {Eip1193Bridge} from '@ethersproject/experimental/lib/eip1193-bridge'

const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')

// address of the above key
export const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address

export const TEST_ADDRESS_NEVER_USE_SHORTENED = `${TEST_ADDRESS_NEVER_USE.substr(
  0,
  6
)}...${TEST_ADDRESS_NEVER_USE.substr(-4, 4)}`

class CustomizedBridge extends Eip1193Bridge {
  chainId = 256

  async sendAsync(...args) {console.debug('sendAsync called', ...args)
    return this.send(...args)
  }
  async send(...args) {console.debug('send called', ...args)
    const isCallbackForm = typeof args[0] === 'object' && typeof args[1] === 'function'
    let callback
    let method
    let params
    if (isCallbackForm) {callback = args[1]
      method = args[0].method
      params = args[0].params
    } else {method = args[0]
      params = args[1]
    }
    if (method === 'eth_requestAccounts' || method === 'eth_accounts') {if (isCallbackForm) {callback({ result: [TEST_ADDRESS_NEVER_USE] })
      } else {return Promise.resolve([TEST_ADDRESS_NEVER_USE])
      }
    }
    if (method === 'eth_chainId') {if (isCallbackForm) {callback(null, { result: '0x100'})
      } else {return Promise.resolve('0x100')
      }
    }
    try {const result = await super.send(method, params)
      console.debug('result received', method, params, result)
      if (isCallbackForm) {callback(null, { result})
      } else {return result}
    } catch (error) {if (isCallbackForm) {callback(error, null)
      } else {throw error}
    }
  }
}

// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
Cypress.Commands.overwrite('visit', (original, url, options) => {return original(url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `${url}` : url, {
    ...options,
    onBeforeLoad(win) {options && options.onBeforeLoad && options.onBeforeLoad(win)
      win.localStorage.clear()
      const provider = new JsonRpcProvider('https://http-testnet.hecochain.com', 256)
      const signer = new Wallet(TEST_PRIVATE_KEY, provider)
      win.ethereum = new CustomizedBridge(signer, provider)
    },
  })
})

参考资料

Cypress 官网文档


欢送区块链行业气味相投的小伙伴增加小极微信,退出 blockgeek 区块链技术交换群,独特推动区块链技术遍及和倒退~

退出移动版