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

比如:

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

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

相关推荐
大柏怎么被偷了19 小时前
【软件测试】测试的岗位有哪些?
软件测试·测试
革斤要加油6 天前
测试开发基础——软件测试中的bug
bug·测试
Amd79410 天前
使用 nuxi upgrade 升级现有nuxt项目版本
测试·开发·命令·升级·nuxt 3·nuxi·项目创建
bug菌¹14 天前
滚雪球学MyBatis-Plus(13):测试与部署
部署·测试·mybatis-plus·零基础入门教学
开测开测21 天前
day31-测试之性能测试工具JMeter的功能概要、元件作用域和执行顺序
测试开发·测试工具·jmeter·单元测试·压力测试·性能测试·测试
开测开测22 天前
day35-测试之性能测试JMeter的测试报告、并发数计算和性能监控
测试开发·测试工具·jmeter·压力测试·性能测试·测试·监控
small_snowball24 天前
三种自动化测试(接口自动化,UI 自动化,单元测试)保姆级教程
java-ee·测试
让开,我要吃人了25 天前
OpenHarmony实战开发: unittest单元测试的编写
linux·华为·log4j·移动开发·测试·harmonyos·鸿蒙
多则惑少则明1 个月前
软件测试第1章 软件测试是什么
测试·测试与质量保证
Serendipity_筱楠1 个月前
解决Pytest UnknownMarkWarning: Unknown pytest.mark.single - is this a typo?
python·pytest·测试