初探 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 和测试环境。但在真实的开发中,引入测试用例还需要考虑很多其他的问题

比如:

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

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

相关推荐
安冬的码畜日常4 天前
【玩转 Postman 接口测试与开发2_007】第六章:Postman 测试脚本的创建(下):预请求脚本及环境变量在多个请求自动运行中的应用
测试工具·postman·测试·runner·api测试·自动测试
北京_宏哥10 天前
《最新出炉》系列入门篇-Python+Playwright自动化测试-50-滚动条操作
python·前端框架·测试
kida_yuan12 天前
【从零开始】6. RAG 应用性能压测工具(番外篇)
后端·llm·测试
孤蓬&听雨17 天前
Kafka自动生产消息软件(自动化测试Kafka)
分布式·kafka·自动化·测试·生产者
帅得不敢出门20 天前
Python+Appium+Pytest+Allure自动化测试框架-安装篇
python·appium·自动化·pytest·测试·allure
陈明勇22 天前
自动化测试在 Go 开源库中的应用与实践
后端·go·测试
帅得不敢出门22 天前
Python+Appium+Pytest+Allure自动化测试框架-代码篇
python·appium·自动化·pytest·测试·allure
Dylanioucn23 天前
《解锁 TDD 魔法:高效软件开发的利器》
后端·功能测试·测试·测试驱动开发·tdd
北京_宏哥23 天前
《最新出炉》系列入门篇-Python+Playwright自动化测试-41-录制视频
前端·python·测试
努力的小雨24 天前
新手入门Java自动化测试的利器:Selenium WebDriver
后端·测试