初探 cypress

简介

cypress 是一个端到端的测试框架,其中的两个"端"分别指的是前端和后端。也就是说,cypress 不仅可以测试前端代码,还能测试接口。但实际使用中,大部分的工作都不会也不应该调用真正的后端接口。而是采用 mock 的方式替代,类似于jest.spy()那种方式去处理接口请求。

Install

lua 复制代码
$ npm install cypress --save-dev
$ npx cypress open

目录

安装完成之后会在根目录下自动生成 cypress 文件夹和 cypress.config.ts 配置文件。

其中 downloads 是测试中下载内容的存放位置

e2e 是存放测试用例的地方

fixtures 是存放各种固定数据的地方

support 是存放一些调整测试环境的脚本的地方

Config

默认情况下,任何报错都会导致测试中止。但对于大型的项目来说,可能出错的地方太多。而我们通常希望测试的范围是很小的。所以建议关掉 Cypress 对这类出错的监控

js 复制代码
Cypress.on('uncaught:exception', (err, runnable) => {
  return false;
})

写一个测试用例

首先需要打开 cypress 的客户端。

arduino 复制代码
$ npx cypress open

当然,你可以把它写在 script 中,改一个你喜欢的命令名字。

我们选择 E2E Testing,如果是首次进入的话,这里会为你自动生成一些默认的配置文件,直接下一步就行。

选择运行环境,这里我们选 chrome。然后点击 「Start E2E Testing in Chrome」。

之后 cypress 会打开一个新的浏览器。

然后直接点击 spec.cy.ts 就可以开始跑测试用例了。

测试用例

cypress 的测试用例的整体结构和其他测试框架都是类似的,都是 describe + it 组成。

js 复制代码
describe('My First Test', () => {
  it('clicks the link "type"', () => {
    cy.visit('https://example.cypress.io')

    cy.contains('type').click()
  })
})

比较不用的是 cypress 的 API。cypress 的 api 按照 jquery 的链式调用设计,用起来很方便,很容易上手。

一个普通的测试用例基本上由四部分构成:

  1. 访问一个路由

值得一提的是,「访问一个路由」可以是访问任意一个路由。并不一定非得是自己的本地项目。

  1. 搜索页面上的某个元素

cypress 和其他流行的测试库一样,除了根据 css 选择器获取元素,也支持根据文字获取元素。

jsx 复制代码
<Button>查询</Button>

//antd 的按钮会自动在文字中间加空格,所以我们也加上。
cy.contains('查 询');
jsx 复制代码
<form>
  <div>
    <label>name</label>
    <input name="name" />
  </div>
  <div>
    <label>age</label>
    <input name="age" />
  </div>
  <input type="submit" value="submit the form!" />
</form>

// 利用 get 缩小搜索范围
cy.get('form').contains('submit the form!').click()
  1. 与这个元素进行某种交互

最常用的就是点击和键盘输入,这两个操作都有现成的 api 可用

js 复制代码
// click
cy.get('.btn').click() // Click on button
cy.focused().click() // Click on el with focus
cy.contains('Welcome').click() // Click on first el containing 'Welcome'

// type
cy.get('input').type('Hello, World') // Type 'Hello, World' into the 'input'
  1. 断言

cypress 封装了 should 和 and 方法,可以在保持链式调用的同时做出断言。

js 复制代码
cy.get('.error').should('be.empty') // Assert that '.error' is empty
cy.contains('Login').should('be.visible') // Assert that el is visible
cy.wrap({ foo: 'bar' }).its('foo').should('eq', 'bar') // Assert the 'foo' property equals 'bar'

stub

cypress 是一个"端到端"的测试框架,所以除了前端逻辑之外,另一个重点就是接口的测试。

cypress 提供 cy.intercept()方法拦截、mock 接口,官方管这个操作叫做"stub",我也不知道该怎么翻译。

stub 这个词一般指的是一个物体剔除掉大部分之后剩下的那部分,比如一根烟抽到最后剩下的烟屁股,就可以叫做这根烟的 "stub";再比如一个树的树桩;铅笔用到最后剩下的铅笔头等等。

引申意思可以指一张票的票根。

我暂且把 stub 翻译成"模拟"

引入请求之后,我们才算得上真正测试一个前后端分离的项目。

cypress 几乎能模拟接口的所有参数,包括强制增加接口的延迟,而且不需要修改源码,速度也很快。

这样我们就可以方便的模拟接口的各种行为,比如接口报错,某个字段为空等常见错误。

js 复制代码
// any request to "/search/*" endpoint will
// automatically receive an array with two book objects
cy.intercept('/search/*', [{ item: 'Book 1' }, { item: 'Book 2' }]).as(
  'getSearch'
)

cy.get('[data-testid="autocomplete"]').type('Book')

// this yields us the interception cycle object
// which includes fields for the request and response
cy.wait('@getSearch')
  .its('request.url')
  .should('include', '/search?query=Book')

cy.get('[data-testid="results"]')
  .should('contain', 'Book 1')
  .and('contain', 'Book 2')

理论上,我们还可以用真实的接口去跑测试用例,但并不推荐这样做。

一个原因是,真实接口不够灵活,没法随意调整更改,况且我们还想测试接口报错这样的场景。

另一方面,预发接口通常不够稳定,也不够快速。用这样的接口有时单纯的开发都有点难受,更别提拿它跑用例了。

但这倒不是说就完全抛弃真实接口,我们当然可以等开发后期,大部分功能都已稳定的时候,再用真实的接口跑一遍用例。

测试思路

知道了如何写测试用例,基本上就可以把 cypress 用起来了,不得不说 cypress 的测试驱动开发的开发体验还不错。

相信大家平常写的较多的还是组件的测试用例,这跟 cypress 提倡的端到端的测试不太一样。

一般组件的测试用例都是针对当前组件的各种边界情况,每个 api 的功能等等。

比如一个日期选择器的 value 属性,如果它的值不合法怎么办?如果它超出了给定的日期范围怎么办?如果不传 value 怎么办?组件是否重新渲染了太多次?等等。都是围绕组件本身展开的。

但在 cypress 的 e2e 测试中,你所面对的是一个完整的应用。

你采取的视角更接近一个真实的测试同学的视角。比如,测试每个筛选项是否生效?表单的校验是否正确?数据的增删改查是否正常?等等。

你的测试用例写起来的过程也更像一个真实的用户的操作流程:

访问页面 => 选择元素 => 与这个元素交互 => 期待应用发生某个行为

总结

cypress 是个不错的端到端的测试框架,提供了不错的 api 和测试环境。但在真实的开发中,引入测试用例还需要考虑很多其他的问题

比如:

  • 编写测试用例的成本?测试用例本质上也是一段代码,也需要精力去编写和调试。是否能找到一个稳定且快速的编写测试用例的方法?
  • 业务需求是否经常变动?如果业务需求经常变动,那之前编写的测试用例可能都需要重新调试。
  • 测试用例的覆盖率?因为是端到端的测试,所以边界情况非常多,为了减少编写测试用例的成本,可能需要判断下,哪些场景需要测试,哪些不需要

综上,对于提升代码质量这个问题,使用测试框架确实能一定程度上的提高代码质量,但在实际工作上,我们需要更全面的根据实际情况考虑这个问题。

相关推荐
song_ly00112 小时前
深入理解软件测试覆盖率:从概念到实践
笔记·学习·测试
试着5 天前
【AI面试准备】掌握常规的性能、自动化等测试技术,并在工作中熟练应用
面试·职场和发展·自动化·测试
waves浪游5 天前
论坛系统测试报告
测试工具·测试用例·bug·测试
灰色人生qwer6 天前
使用JMeter 编写的测试计划的多个线程组如何生成独立的线程组报告
jmeter·测试
.格子衫.6 天前
powershell批处理——io校验
测试·powershell
试着7 天前
【AI面试准备】TensorFlow与PyTorch构建缺陷预测模型
人工智能·pytorch·面试·tensorflow·测试
waves浪游7 天前
博客系统测试报告
测试工具·测试用例·bug·测试
智云软件测评服务9 天前
数字化时代下,软件测试中的渗透测试是如何保障安全的?
渗透·测试·漏洞
试着10 天前
【AI面试准备】XMind拆解业务场景识别AI赋能点
人工智能·面试·测试·xmind
waves浪游11 天前
性能测试工具篇
测试工具·测试用例·bug·测试