Skip to main content

权衡取舍

Cypress通过其独特的架构实现浏览器自动化。虽然这赋予了它无与伦比的能力,但也带来了一些特定的权衡取舍。

本指南将列出这些权衡点,并具体说明如何应对它们。

虽然乍看之下这些似乎是Cypress的严格限制,但您很快会意识到其中许多边界实际上是有益的。从某种意义上说,它们能防止您编写糟糕、缓慢或不稳定的测试。

永久性权衡概述

临时性权衡概述

我们想重点说明一些Cypress希望最终解决的临时限制:

永久性权衡

自动化限制

Cypress应用是一个专注于端到端测试、组件测试和API测试的专用工具。我们认为Cypress不适合以下场景:

  • 网络索引
  • 链接爬取
  • 性能测试
  • 第三方站点脚本

对于上述每个场景,都有其他更优秀的专用工具。

Cypress的核心价值在于作为测试您自己应用程序的工具。

浏览器内部

再次强调——Cypress测试在浏览器内部运行!这意味着我们可以做到其他工具无法实现的事情。没有对象序列化或JSON有线协议,您可以直接访问被测应用中的所有内容。

但这也意味着您的测试代码是在浏览器中执行的。测试代码不是在Node或任何其他服务端语言中评估的。我们唯一支持的语言就是Web语言:JavaScript。

这种权衡使得与后端(如服务器或数据库)的通信稍具挑战性。您无法直接连接或import那些服务端库或模块。不过您可以引入能在浏览器中使用的node_modules。此外,您还可以通过Node事件cy.task()使用Node直接与后端脚本交互。

要与数据库或服务器通信,您需要使用cy.exec()cy.task()cy.request()命令。这意味着您需要提供种子数据和设置数据库的方法。这可能比其他用后端语言编写的测试工具需要更多额外工作。

这里的权衡在于,在浏览器中执行所有操作能为Cypress带来更好的体验,而在浏览器外执行操作可能需要更多额外工作。

同时打开多个浏览器

Cypress不支持同时控制多个打开的浏览器。但您可以使用@cypress/puppeteer插件测试多个标签页。

尽管如此,除了极少数特殊情况外,您仍然可以在不打开多个浏览器的情况下测试大多数应用行为。

您可能会这样询问此功能:

我正在测试一个聊天应用。能否用Cypress同时运行多个浏览器?

无论您测试的是聊天应用还是其他功能,实际上您关注的是测试协作。但您不需要重建整个环境来实现100%覆盖率的协作测试。

以下方法可能更快、更准确且更具扩展性。

虽然超出本文范围,但您可以使用以下原则测试聊天应用。每个原则都会逐步引入更多协作:

1. 仅使用浏览器:


← 浏览器 →

避开服务器,手动调用JavaScript回调来模拟"通知到达"或"用户离开聊天"等场景。

您可以stub所有内容并模拟每个场景:聊天消息、离线消息、连接、重连、断开连接、群聊等。浏览器内发生的所有内容都可以完全测试。离开浏览器的请求也可以被stub,并断言请求体是否正确。

2. Stub其他连接:

服务器 → 浏览器

服务器 ← 浏览器

(其他连接被stub)

服务器 → 浏览器

使用服务器接收来自浏览器的消息,并通过以该参与者身份发送消息来模拟"其他参与者"。这取决于具体应用,但通常可以通过向数据库插入记录等方式,使服务器表现得像需要将一个客户端的消息发送回浏览器。

这种模式通常可以避免建立第二个WebSocket连接,同时仍满足浏览器和服务器的双向契约。这意味着您还可以在不处理真实连接的情况下测试边缘情况(如断开连接等)。

3. 引入另一个连接:

服务器 → 浏览器

服务器 ← 浏览器

服务器 → 其他连接

服务器 ← 其他连接

服务器 → 浏览器

要实现这一点,您需要一个浏览器外的后台进程来建立底层WebSocket连接,然后您可以与之通信并控制它。

有多种方法可以实现,这里是一个使用HTTP服务器作为客户端并暴露REST接口的示例:

// Cypress测试

// 告诉8081端口的HTTP服务器连接到8080
cy.request('http://localhost:8081/connect?url=http://localhost:8080')

// 告诉8081端口的HTTP服务器发送消息
cy.request('http://localhost:8081/message?m=hello')

// 告诉8081端口的HTTP服务器断开连接
cy.request('http://localhost:8081/disconnect')

HTTP服务器代码大致如下:

const client = require('socket.io:client')
const express = require('express')

const app = express()

let socket

app.get('/connect', (req, res) => {
const url = req.query.url

socket = client(url)

socket.on('connect', () => {
res.sendStatus(200)
})
})

app.get('/message', (req, res) => {
const msg = req.query.m

socket.send(msg, () => {
res.sendStatus(200)
})
})

app.get('/disconnect', (req, res) => {
socket.on('disconnect', () => {
res.sendStatus(200)
})

socket.disconnect()
})

app.listen(8081, () => {})

这种方法避免了需要第二个打开的浏览器,同时仍提供了端到端测试,确保两个客户端可以相互通信。