intercept
监控和存根网络请求与响应。
提示:建议先阅读网络请求指南。
所有拦截器在每个测试前会自动清除。
语法
// 仅监控
cy.intercept(url)
cy.intercept(method, url)
cy.intercept(routeMatcher)
参数参见 url、 method 和 routeMatcher
// 监控并存根响应
cy.intercept(url, staticResponse)
cy.intercept(method, url, staticResponse)
cy.intercept(routeMatcher, staticResponse)
cy.intercept(url, routeMatcher, staticResponse)
参见 staticResponse 参数
// 监控、动态存根、请求修改等
cy.intercept(url, routeHandler)
cy.intercept(method, url, routeHandler)
cy.intercept(routeMatcher, routeHandler)
cy.intercept(url, routeMatcher, routeHandler)
参见 routeHandler 参数
// 指定请求和响应类型
type CustomRequest = {
kind: 'custom_request'
}
type CustomResponse = {
kind: 'custom_response'
}
cy.intercept<CustomRequest, CustomResponse>(url, (req) => {
req.body // 请求的.body将是CustomRequest类型
req.continue((res) => {
res.body // 响应的.body将是CustomResponse类型
})
})
用法
正确用法
// 监控
cy.intercept('/users/**')
cy.intercept('GET', '/users*')
cy.intercept({
method: 'GET',
url: '/users*',
hostname: 'localhost',
})
// 监控并存根响应
cy.intercept('POST', '/users*', {
statusCode: 201,
body: {
name: 'Peter Pan',
},
})
// 监控、动态存根、请求修改等
cy.intercept('/users*', { hostname: 'localhost' }, (req) => {
/* 对请求和/或响应进行操作 */
})
参数
method (String)
匹配特定的HTTP方法(GET
、
POST
、PUT
等)。
如果未定义方法,Cypress默认会匹配所有请求。
url (String, Glob, RegExp)
指定要匹配的URL。示例参见匹配url
。
或者,通过routeMatcher
参数指定URL。
routeMatcher (RouteMatcher
)
routeMatcher
是一个对象,用于将传入的HTTP请求与拦截的路由匹配。
所有属性都是可选的,但设置的属性必须全部匹配才能拦截请求。如果向任何属性传递string
,它将使用Cypress.minimatch
和{ matchBase: true }
选项进行全局匹配。
选项 | 描述 |
---|---|
auth | HTTP基本认证(包含username 和password 键的对象) |
headers | HTTP请求头(对象) |
hostname | HTTP请求主机名 |
https | true :仅匹配安全(https://)请求,false :仅匹配非安全(http://)请求 |
method | HTTP请求方法(默认匹配任何方法) |
middleware | true :按定义顺序优先匹配路由,false :反向匹配路由(默认) |
path | HTTP请求路径(主机名后,包含查询参数) |
pathname | 类似path ,但不包含查询参数 |
port | HTTP请求端口(数字或数组) |
query | 解析后的查询字符串(对象) |
resourceType 已弃用 | 请求的资源类型。resourceType 可能值列表参见"请求对象属性"。 |
times | 最大匹配次数(数字) |
url | 完整 的HTTP请求URL |
示例参见下文使用RouteMatcher。
staticResponse (StaticResponse
)
通过传递StaticResponse
作为最后一个参数,可以为匹配的请求静态定义(存根)响应。属性列表参见StaticResponse
对象。
此外,可以在StaticResponse
中传递{ log: false }
来禁用此拦截的命令日志。参见禁用请求日志。
routeHandler (Function
)
每当请求匹配时,会调用routeHandler
函数,第一个参数是请求对象。在回调中,可以访问整个请求-响应周期,修改传出请求、发送响应、访问真实响应等。
参见"拦截的请求"和使用routeHandler
修改请求/响应。
生成结果
示例
匹配url
可以提供精确的URL进行匹配,或使用模式匹配一次匹配多个URL,可以是glob或正则表达式。参见URL的Glob模式匹配。
// 匹配完全符合URL的任何请求
cy.intercept('https://prod.cypress.io/users')
// 匹配满足glob模式的任何请求
cy.intercept('/users?_limit=*')
// 匹配满足正则模式的任何请求
cy.intercept(/\/users\?_limit=(3|5)$/)
匹配method
如果不传递method
参数,则会匹配所有HTTP方法(GET
、POST
、PUT
、PATCH
、DELETE
等)。
cy.intercept('/users')
// 匹配: GET http://localhost/users
// 也匹配: POST http://localhost/users
cy.intercept('GET', '/users')
// 匹配: GET http://localhost/users
// 但不匹配: POST http://localhost/users
使用RouteMatcher匹配
通过向cy.intercept
传递routeMatcher
对象,也可以实现指定method
和url
进行匹配:
// 两者结果相同:
cy.intercept({ method: 'GET', url: '**/users' })
cy.intercept('GET', '**/users')
// 匹配路径名为`/search`且查询参数为'q=some+terms'的任何类型请求
cy.intercept({
pathname: '/search',
query: {
q: 'some terms',
},
}).as('searchForTerms')
cy.intercept(
{
// 此正则匹配任何以'http://api.example.com/'开头并以'/edit'或'/save'结尾的URL
url: /^http:\/\/api\.example\.com\/.*\/(edit|save)/,
// 匹配的请求还必须包含此请求头
headers: {
'x-requested-with': 'exampleClient',
},
}
)
// 此示例将使1次对`/temporary-error`的请求收到网络错误,后续请求将不匹配此`RouteMatcher`
cy.intercept({ url: '/temporary-error', times: 1 }, { forceNetworkError: true })
模式匹配
// 使用glob匹配更新`/users`端点
cy.intercept({
method: '+(PUT|PATCH)',
url: '**/users/*',
})
// 匹配:
// PUT /users/1
// PATCH /users/1
//
// 不匹配:
// GET /users
// GET /users/1
// 同上,但使用正则
cy.intercept({
method: '/PUT|PATCH/',
url: '**/users/*',
})
为拦截的路由设置别名
虽然cy.intercept
不返回任何内容,但可以链式调用.as
创建别名,用于等待请求。
cy.intercept('GET', '/users').as('getAllUsers')
cy.intercept('POST', '/users').as('createUser')
为单个请求设置别名
可以通过设置拦截请求的alias
属性为每个请求设置别名。这在拦截GraphQL请求时特别有用:
cy.intercept('POST', '/graphql', (req) => {
if (req.body.hasOwnProperty('query') && req.body.query.includes('mutation')) {
req.alias = 'gqlMutation'
}
})
// 断言已发出匹配的请求
cy.wait('@gqlMutation')
有关GraphQL请求别名的更多指导,参见使用GraphQL。
等待请求
使用cy.wait()和为拦截的路由设置别名等待请求/响应周期完成。
使用URL
cy.intercept('http://example.com/settings').as('getSettings')
// 一旦对获取设置的请求响应,'cy.wait'将解析
cy.wait('@getSettings')
使用RouteMatcher
cy.intercept({
url: 'http://example.com/search*',
query: { q: 'expected terms' },
}).as('search')
// 一 旦任何类型的搜索请求(查询字符串包含'q=expected+terms')响应,'cy.wait'将解析
cy.wait('@search')
使用返回的对象
在cy.intercept()
路由别名上使用cy.wait()会返回一个表示请求/响应周期的拦截对象:
cy.wait('@someRoute').then((interception) => {
// 'interception'是一个包含'id'、'request'和'response'属性的对象
})
可以链式调用.its()
和.should()
对请求/响应周期进行断言:
// 断言对此路由的请求体包含'user'
cy.wait('@someRoute').its('request.body').should('include', 'user')
// 断言对此路由的请求收到HTTP状态码500的响应
cy.wait('@someRoute').its('response.statusCode').should('eq', 500)
// 断言对此路由的请求收到包含'id'的响应体
cy.wait('@someRoute').its('response.body').should('include', 'id')
等待错误
可以使用cy.wait()等待以网络错误结束的请求:
cy.intercept('GET', '/should-err', { forceNetworkError: true }).as('err')
// 断言此请求发生并以错误结束
cy.wait('@err').should('have.property', 'error')
存根响应
使用字符串
// 对'/update'的请求将以"success"作为响应体完成
cy.intercept('/update', 'success')
使用fixture
// 对'/users.json'的请求将以"users.json" fixture的内容完成
cy.intercept('/users.json', { fixture: 'users.json' })
使用StaticResponse
对象
StaticResponse
对象表示对HTTP请求的响应,可用于存根路由:
const staticResponse = {
/* 此处为一些StaticResponse属性... */
}
cy.intercept('/projects', staticResponse)
使用JSON体存根响应:
cy.intercept('/projects', {
body: [{ projectId: '1' }, { projectId: '2' }],
})
同时存根头、状态码和体:
cy.intercept('/not-found', {
statusCode: 404,
body: '404 Not Found!',
headers: {
'x-not-found': 'true',
},
})
使用读取为Buffer的fixture存根响应:
cy.intercept('/not-found', {
fixture: 'media/gif.mp4,null',
})
使用**routeHandler
**函数
通过向cy.intercept
的最后一个参数指定routeHandler
函数,可以访问整个请求-响应会话,从而能够修改传出请求、操作真实响应、进行断言等。
routeHandler
将传入的HTTP请求(IncomingHTTPRequest
)作为第一个参数。
cy.intercept('/users*', (req) => {
/* 对请求和/或响应进行操作 */
})
在这些示例中,我们将传入的HTTP请求称为req
。熟悉Express.js中间件语法的用户应该对此语法感到熟悉。
对请求进行断言
cy.intercept('POST', '/organization', (req) => {
expect(req.body).to.include('Acme Company')
})
修改传出请求
可以使用请求处理程序回调在发送之前修改拦截的请求对象。
// 在发送到目标之前将请求体设置为不同的内容
cy.intercept('POST', '/login', (req) => {
req.body = 'username=janelane&password=secret123'
})
// 动态设置别名
cy.intercept('POST', '/login', (req) => {
req.alias = 'login'
})
向传出请求添加头
可以向传出请求添加头,或修改现有头
cy.intercept('/req-headers', (req) => {
req.headers['x-custom-headers'] = 'added by cy.intercept'
})
注意:新头不会显示在浏览器的网络标签中,因为请求已经离开浏览器。仍然可以通过如下等待拦截来确认头已添加:
等待拦截
cy.intercept('/req-headers', (req) => {
req.headers['x-custom-headers'] = 'added by cy.intercept'
}).as('headers')
// 应用程序发出调用...
// 确认自定义头已添加
cy.wait('@headers')
.its('request.headers')
.should('have.property', 'x-custom-headers', 'added by cy.intercept')
向所有传出请求添加、修改或删除头
可以使用supportFile中的beforeEach()
向所有传出请求添加、修改或删除头。
beforeEach(() => {
cy.intercept(
{ url: 'http://localhost:3001/**', middleware: true },
// 从所有传出请求中删除'if-none-match'头
(req) => delete req.headers['if-none-match']
)
})
动态存根响应
可以使用req.reply()
函数动态控制对请求的响应。
cy.intercept('/billing', (req) => {
// 'req'上的函数可用于在此动态响应请求
// 将请求发送到目标服务器
req.reply()
// 用JSON对象响应请求
req.reply({ plan: 'starter' })
// 将请求发送到目标服务器并拦截响应
req.continue((res) => {
// 'res'表示真实目标的响应
// 更多详情和示例参见"拦截响应"
})
})
更多关于req
对象及其属性和方法的信息,参见"拦截的请求"。
返回Promise
如果从路由回调返回Promise,它将在继续请求之前被等待。
cy.intercept('POST', '/login', (req) => {
// 可以异步获取测试数据...
return getLoginCredentials().then((credentials) => {
// ...然后使用它补充传出请求
req.headers['authorization'] = credentials
})
})
将请求传递给下一个请求处理程序
如果在请求处理程序中没有显式调用req.reply()
或req.continue()
,请求将传递给下一个请求处理程序,直到没有剩余。
// 可以有一个顶层的中间件处理程序,在所有请求上设置认证令牌
// 但记住设置`middleware: true`会导致它总是首先被调用
cy.intercept('http://api.company.com/', { middleware: true }, (req) => {
req.headers['authorization'] = `token ${token}`
})
// 然后有另一个更窄地断言某些请求的处理程序
cy.intercept('POST', 'http://api.company.com/widgets', (req) => {
expect(req.body).to.include('analytics')
})
// 对http://api.company.com/widgets的POST请求会触发这两个回调,中间件优先,然后请求会带着修改后的请求头发送到真实目标
禁用请求的日志
默认情况下,Cypress会记录匹配任何cy.intercept()
的所有请求,以及所有XMLHttpRequest
和fetch
请求。可以通过在第二个参数中传递{ log: false }
来使用cy.intercept()
禁用这些日志:
// 禁用Cypress默认记录所有XMLHttpRequest和fetch的行为
cy.intercept({ resourceType: /xhr|fetch/ }, { log: false })
注意:目前只能在定义cy.intercept()
时启用/禁用请求的日志,不能在intercept
回调中。参见#26069。
拦截响应
在传递给req.continue()
的回调中,可以访问目标服务器的真实响应。
cy.intercept('/integrations', (req) => {
// 带回调的req.continue()会将请求发送到目标服务器
req.continue((res) => {
// 'res'表示真实目标的响应
// 可以在发送到浏览器之前操作'res'
})
})
更多关于res
对象的信息,参见"拦截的响应"。更多关于req.continue()
的信息,参见"使用req.continue()
控制传出请求"