перехват
Подмена и имитация сетевых запросов и ответов.
Подсказка: Рекомендуется сначала ознакомиться с руководством Сетевые запросы.
cy.intercept()является преемникомcy.route()начиная с Cypress 6.0.0. См. Сравнение сcy.route.
Все перехваты автоматически очищаются перед каждым тестом.
Синтаксис
// spying only
cy.intercept(url)
cy.intercept(method, url)
cy.intercept(routeMatcher)
См. аргументы url, method и routeMatcher
// spying and response stubbing
cy.intercept(url, staticResponse)
cy.intercept(method, url, staticResponse)
cy.intercept(routeMatcher, staticResponse)
cy.intercept(url, routeMatcher, staticResponse)
См. аргумент staticResponse
// spying, dynamic stubbing, request modification, etc.
cy.intercept(url, routeHandler)
cy.intercept(method, url, routeHandler)
cy.intercept(routeMatcher, routeHandler)
cy.intercept(url, routeMatcher, routeHandler)
См. аргумент routeHandler
Использование
Правильное использование
// spying
cy.intercept('/users/**')
cy.intercept('GET', '/users*')
cy.intercept({
method: 'GET',
url: '/users*',
hostname: 'localhost',
})
// spying and response stubbing
cy.intercept('POST', '/users*', {
statusCode: 201,
body: {
name: 'Peter Pan',
},
})
// spying, dynamic stubbing, request modification, etc.
cy.intercept('/users*', { hostname: 'localhost' }, (req) => {
/* do something with request and/or response */
})
Аргументы
method (String)
Сопоставьте маршрут со специфическим HTTP-методом (GET, POST, PUT, и т.д.).
Если метод не определен, Cypress по умолчанию будет сопоставлять все запросы.
url (String, Glob, RegExp)
Укажите URL для сопоставления. См. Сопоставление url для примеров.
В качестве альтернативы укажите URL с помощью аргумента routeMatcher (ниже).
routeMatcher (RouteMatcher)
routeMatcher — это объект, используемый для сопоставления входящих HTTP-запросов с этим перехваченным маршрутом.
Все свойства необязательны, но все установленные свойства должны совпадать для перехвата запроса. Если для любого свойства передается string, оно будет сопоставляться с запросом с использованием Cypress.minimatch с применением параметра { matchBase: true } minimatch.
| Опция | Описание |
|---|---|
| auth | HTTP Basic аутентификация (object с ключами username и password) |
| headers | HTTP заголовки запроса (object) |
| hostname | HTTP имя хоста запроса |
| https |
true: только защищенные (https://) запросы, false: только незащищенные (http://) запросы |
| method | HTTP метод запроса (по умолчанию сопоставляется любой метод) |
| middleware |
true: сначала сопоставить маршрут в определенном порядке, false: сопоставить маршрут в обратном порядке (по умолчанию) |
| path | HTTP путь запроса после имени хоста, включая параметры запроса |
| pathname | Как path, но без параметров запроса |
| port | HTTP порт запроса(ов) (number или Array) |
| query | Разбор строки запроса (object) |
| times | Максимальное количество совпадений (number) |
| url | Полный URL HTTP запроса |
См. примеры ниже.
staticResponse (StaticResponse)
Передав StaticResponse в качестве последнего аргумента, вы можете статично определить (имитировать) ответ для сопоставленных запросов. См. StaticResponse объект для списка свойств.
См. Имитация ответа с помощью StaticResponse объекта для примера.
routeHandler (Function)
Функция routeHandler вызывается всякий раз, когда запрос сопоставляется, первым аргументом является объект запроса. Изнутри обратного вызова у вас есть доступ ко всему сеансу запрос-ответ, где вы можете изменить исходящий запрос, отправить ответ, получить доступ к реальному ответу и многое другое.
См. "Перехваченные запросы" и Изменение запроса/ответа с помощью routeHandler.
Возвращаемые значения
-
cy.intercept()возвращаетnull. -
cy.intercept()может быть переименован, но в противном случае не может быть дальше цепочечно связан. - Ожидание переименованного
cy.intercept()маршрута с помощью cy.wait() вернет объект, содержащий информацию о цикле запроса/ответа. См. Использование возвращаемого объекта для примеров использования этого объекта.
Примеры
cy.interceptможет использоваться только для наблюдения: пассивного прослушивания сопоставленных маршрутов и применения псевдонимов к ним без изменения запроса или его ответа каким-либо образом. Это само по себе мощно, так как позволяет ждать этих запросов, что приводит к более надежным тестам.
Сопоставление url
Вы можете указать точный URL для сопоставления или использовать сопоставление по образцу для сопоставления множества URL-адресов одновременно, либо с использованием шаблонов, либо с использованием регулярных выражений. См. Сопоставление URL-адресов по шаблонам.
// match any request that exactly matches the URL
cy.intercept('https://prod.cypress.io/users')
// match any request that satisfies a glob pattern
cy.intercept('/users?_limit=*')
// match any request that satisfies a regex pattern
cy.intercept(/\/users\?_limit=(3|5)$/)
Сопоставление method
Если вы не передаете аргумент
method, то все HTTP-методы (GET,POST,PUT,PATCH,DELETE, и т.д.) будут сопоставляться.
cy.intercept('/users')
// matches this: GET http://localhost/users
// ...and this, too: POST http://localhost/users
cy.intercept('GET', '/users')
// matches this: GET http://localhost/users
// ...but not this: POST http://localhost/users
Сопоставление с RouteMatcher
Указание method и url для сопоставления также можно выполнить, передав объект routeMatcher в cy.intercept вместо этого:
// These both yield the same result:
cy.intercept({ method: 'GET', url: '**/users' })
cy.intercept('GET', '**/users')
// Match any type of request with the pathname `/search`
// and the query paramater 'q=some+terms'
cy.intercept({
pathname: '/search',
query: {
q: 'some terms',
},
}).as('searchForTerms')
cy.intercept(
{
// this RegExp matches any URL beginning with
// 'http://api.example.com/' and ending with '/edit' or '/save'
url: /^http:\/\/api\.example\.com\/.*\/(edit|save)/,
// matching requests must also contain this header
headers: {
'x-requested-with': 'exampleClient',
},
}
})
// this example will cause 1 request to `/temporary-error`
// to receive a network error and subsequent requests will
// not match this `RouteMatcher`
cy.intercept('/temporary-error', { times: 1 }, { forceNetworkError: true })
Сопоставление по образцу
// match updates to the `/users` endpoint using glob matching
cy.intercept({
method: '+(PUT|PATCH)',
url: '**/users/*',
})
// matches:
// PUT /users/1
// PATCH /users/1
// doesn't match
// GET /users
// GET /users/1
// same as above, but using regex
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'
}
})
// assert that a matching request has been made
cy.wait('@gqlMutation')
Дополнительные рекомендации по присвоению псевдонимов запросам GraphQL см. в разделе Работа с GraphQL
Ожидание запроса
Используйте cy.wait() с присвоением псевдонима перехваченному маршруту, чтобы дождаться завершения цикла запрос/ответ.
С URL
cy.intercept('http://example.com/settings').as('getSettings')
// once a request to get settings responds, 'cy.wait' will resolve
cy.wait('@getSettings')
С RouteMatcher
cy.intercept({
url: 'http://example.com/search*',
query: { q: 'expected terms' },
}).as('search')
// once any type of request to search with a querystring
// containing 'q=expected+terms' responds, 'cy.wait' will resolve
cy.wait('@search')
Использование возвращаемого объекта
Использование cy.wait() для маршрута с псевдонимом cy.intercept() возвращает объект перехвата, который представляет цикл запрос/ответ:
cy.wait('@someRoute').then((interception) => {
// 'interception' is an object with properties
// 'id', 'request' and 'response'
})
Вы можете прицепить .its() и .should() для утверждения относительно циклов запроса/ответа:
// assert that a request to this route
// was made with a body that included 'user'
cy.wait('@someRoute').its('request.body').should('include', 'user')
// assert that a request to this route
// received a response with HTTP status 500
cy.wait('@someRoute').its('response.statusCode').should('eq', 500)
// assert that a request to this route
// received a response body that includes 'id'
cy.wait('@someRoute').its('response.body').should('include', 'id')
Ожидание ошибок
Вы можете использовать cy.wait() для ожидания запросов, которые завершаются сетевыми ошибками:
cy.intercept('GET', '/should-err', { forceNetworkError: true }).as('err')
// assert that this request happened
// and that it ended in an error
cy.wait('@err').should('have.property', 'error')
Имитация ответа
С строкой
// requests to '/update' will be fulfilled
// with a body of "success"
cy.intercept('/update', 'success')
С фикстурой
// requests to '/users.json' will be fulfilled
// with the contents of the "users.json" fixture
cy.intercept('/users.json', { fixture: 'users.json' })
С объектом StaticResponse
Объект StaticResponse представляет собой ответ на HTTP-запрос и может использоваться для имитации маршрутов:
const staticResponse = {
/* some StaticResponse properties here... */
}
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',
},
})
Имитация ответа с фикстурой, которая читается как буфер:
cy.intercept('/not-found', {
fixture: 'media/gif.mp4,null',
})
См. также StaticResponse объект.
Использование функции routeHandler
Указав функцию routeHandler в качестве последнего аргумента к cy.intercept, вы получите доступ ко всему сеансу запрос-ответ, что позволит изменять исходящий запрос, изменять реальный ответ, выполнять утверждения и т.д.
Функция routeHandler принимает входящий HTTP-запрос (IncomingHTTPRequest) в качестве первого аргумента.
cy.intercept('/users*', (req) => {
/* do something with request and/or response */
})
В этих примерах мы будем ссылаться на входящий HTTP-запрос как на
req. Те из вас, кто знаком с Express.js средствами промежуточного уровня, должны быть знакомы с этим синтаксисом.
Проверка запроса
cy.intercept('POST', '/organization', (req) => {
expect(req.body).to.include('Acme Company')
})
Модификация исходящего запроса
Вы можете использовать обратный вызов обработчика запросов для модификации объекта перехваченного запроса перед его отправкой.
// set the request body to something different
// before it's sent to the destination
cy.intercept('POST', '/login', (req) => {
req.body = 'username=janelane&password=secret123'
})
// dynamically set the alias
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')
// the application makes the call ...
// confirm the custom header was added
cy.wait('@headers')
.its('request.headers')
.should('have.property', 'x-custom-headers', 'added by cy.intercept')
Добавление, изменение или удаление заголовка для всех исходящих запросов
Вы можете добавить, изменить или удалить заголовок для всех исходящих запросов, используя beforeEach() в файле cypress/support/index.js.
// cypress/support/index.ts
beforeEach(() => {
cy.intercept(
{ url: 'http://localhost:3001/**', middleware: true },
// Delete 'if-none-match' header from all outgoing requests
(req) => delete req.headers['if-none-match']
)
})
Динамическое создание заглушки ответа
Вы можете использовать функцию req.reply() для динамического управления ответом на запрос.
cy.intercept('/billing', (req) => {
// functions on 'req' can be used to
// dynamically respond to a request here
// send the request to the destination server
req.reply()
// respond to the request with a JSON object
req.reply({ plan: 'starter' })
// send the request to the destination server
// and intercept the response
req.continue((res) => {
// 'res' represents the real destination's response
// See "Intercepting a response" for more details and examples
})
})
См. «Перехваченные запросы» для получения дополнительной информации об объекте req и его свойствах и методах.
Возврат Promise
Если из обратного вызова маршрута возвращается Promise, он будет ожидать его выполнения перед продолжением обработки запроса.
cy.intercept('POST', '/login', (req) => {
// you could asynchronously fetch test data...
return getLoginCredentials().then((credentials) => {
// ...and then, use it to supplement the outgoing request
req.headers['authorization'] = credentials
})
})
Передача запроса следующему обработчику запросов
Если req.reply() или req.continue() не вызывается явно внутри обработчика запроса, запросы будут передаваться следующему обработчику запросов, пока не останется ни одного.
// you could have a top-level middleware handler that
// sets an auth token on all requests
// but remember setting `middleware: true` will
// cause this to always be called first
cy.intercept('http://api.company.com/', { middleware: true }, (req) => {
req.headers['authorization'] = `token ${token}`
})
// and then have another handler that
// more narrowly asserts on certain requests
cy.intercept('POST', 'http://api.company.com/widgets', (req) => {
expect(req.body).to.include('analytics')
})
// a POST request to http://api.company.com/widgets would hit both
// of those callbacks, middleware first, then the request would be
// sent out with the modified request headers to the
// real destination
Перехват ответа
Внутри обратного вызова, переданного в req.continue(), вы можете получить доступ к реальному ответу целевого сервера.
cy.intercept('/integrations', (req) => {
// req.continue() with a callback will send the request to
// the destination server
req.continue((res) => {
// 'res' represents the real destination response
// you can manipulate 'res' before it's sent to the browser
})
})
См. «Перехваченные ответы» для получения дополнительной информации об объекте res . См. «Управление исходящим запросом с помощью req.continue()» для получения дополнительной информации об req.continue().
Проверка ответа
cy.intercept('/projects/2', (req) => {
req.continue((res) => {
expect(res.body).to.include('My Project')
})
})
Возврат Promise
Если из обратного вызова маршрута возвращается Promise, он будет ожидать его выполнения перед отправкой ответа в браузер.
cy.intercept('/users', (req) => {
req.continue((res) => {
// the response will not be sent to the browser until
// 'waitForSomething()' resolves
return waitForSomething()
})
})
Ограничение или задержка ответа на все входящие ответы
Вы можете ограничить или замедлить все входящие ответы, используя beforeEach() в файле cypress/support/index.js.
// cypress/support/index.ts
// Throttle API responses to simulate real-world conditions
cy.intercept(
{
url: 'http://localhost:3001/**',
middleware: true,
},
(req) => {
req.on('response', (res) => {
// Throttle the response to 1 Mbps to simulate a
// mobile 3G connection
res.setThrottle(1000)
})
}
)
Модификация запроса/ответа с помощью routeHandler
Укажите routeHandler в качестве последнего аргумента для модификации исходящего запроса, создания заглушки ответа, выполнения утверждений и т. д.
Если в качестве routeHandler передана функция, она будет вызвана с перехваченным HTTP-запросом:
cy.intercept('/api', (req) => {
// do something with the intercepted request
})
Отсюда вы можете выполнить несколько действий с перехваченным запросом:
- изменять и проверять свойства запроса, такие как тело, заголовки, URL, метод и т. д. (пример)
- создавать заглушку ответа без взаимодействия с реальным бэкэндом (пример
- передавать запрос по назначению и изменять или проверять реальный ответ по пути обратно (пример)
- прикреплять обработчики к различным событиям запроса (пример)
Проверка запроса
Вы можете использовать обратный вызов обработчика запросов для проверки объекта перехваченного запроса перед его отправкой.
// match requests to create a user
cy.intercept('POST', '/users', (req) => {
// make an assertion on the payload contents
expect(req.body).to.include('Peter Pan')
})
Управление исходящим запросом
Исходящий запрос, включая его тело, заголовки и т. д., может быть изменен перед отправкой.
// modify the request body before it's sent to its destination
cy.intercept('POST', '/users', (req) => {
req.body = {
name: 'Peter Pan',
}
})
// add a header to an outgoing request
cy.intercept('POST', '/users', (req) => {
req.headers['x-custom-header'] = 'added by cy.intercept'
})
// modify an existing header
cy.intercept('POST', '/users', (req) => {
req.headers['authorization'] = 'Basic YWxhZGRpbjpvcGVuc2VzYW1l'
})
Проверка изменения запроса
cy.intercept('POST', '/users', (req) => {
req.headers['x-custom-header'] = 'added by cy.intercept'
}).as('createUser')
cy.get('button.save').click()
// you can see the headers in the console output by selecting
// this line in the command log:
cy.wait('@createUser')
// ...or make an assertion:
.its('request.headers')
.should('have.property', 'x-custom-header', 'added by cy.intercept')
Изменение запроса нельзя проверить, просматривая сетевой трафик браузера (например, в Chrome DevTools), так как браузер регистрирует сетевой трафик *до* того, как Cypress может его перехватить.
cy.intercept()нельзя отлаживать с помощьюcy.request()! Cypress перехватывает только запросы, отправленные вашим front-end приложением.
Управление ответом
Перехваченный запрос, переданный обработчику маршрута (в дальнейшем обозначаемый как req, хотя вы можете использовать любое имя), содержит методы для динамического управления ответом на запрос:
-
req.reply()— создание заглушки ответа, не требующей зависимости от реального бэкэнда -
req.continue()— изменение или проверка реального ответа -
req.destroy()— уничтожение запроса и предоставление сетевой ошибки -
req.redirect()— ответ на запрос с перенаправлением на указанный адрес -
req.on()— изменение ответа, подключаясь к событиям
Создание заглушки ответа (req.reply()):
req.reply() принимает объект StaticResponse в качестве первого аргумента:
// stub out the response without interacting with a real back-end
cy.intercept('POST', '/users', (req) => {
req.reply({
headers: {
Set-Cookie: 'newUserName=Peter Pan;'
},
statusCode: 201,
body: {
name: 'Peter Pan'
},
delay: 10, // milliseconds
throttleKbps: 1000, // to simulate a 3G connection
forceNetworkError: false // default
})
})
// stub out a response body using a fixture
cy.intercept('GET', '/users', (req) => {
req.reply({
statusCode: 200, // default
fixture: 'users.json'
})
})
См. StaticResponse ниже для получения дополнительной информации.
Метод reply также поддерживает сокращённую запись, чтобы избежать необходимости указывать объект StaticResponse.
// equivalent to `req.reply({ body })`
req.reply(body)
// equivalent to `req.reply({ body, headers })`
req.reply(body, headers)
// equivalent to `req.reply({ statusCode, body, headers})`
req.reply(statusCode, body, headers)
Примечание: Вызов
reply()завершит фазу запроса и прекратит распространение запроса к следующему обработчику запросов в очереди. См. Жизненный цикл перехвата.
См. также Предоставление заглушки ответа с req.reply()
Изменение реального ответа (continue):
Метод continue принимает функцию, которой передается объект, представляющий реальный ответ, перехваченный по пути обратно к клиенту (вашему front-end приложению).
// pass the request through and make an assertion on
// the real response
cy.intercept('POST', '/users', (req) => {
req.continue((res) => {
expect(res.body).to.include('Peter Pan')
})
})
См. также Управление исходящим запросом с помощью req.continue()
Ответ с сетевой ошибкой (destroy):
// dynamically destroy the request and
// respond with a network error
cy.intercept('POST', '/users', (req) => {
if (mustDestroy(req)) {
req.destroy()
}
function mustDestroy(req) {
// code that determines whether to force a network error
// based on the contents of `req`
}
})
Ответ с новым местоположением (redirect):
// respond to this request with a redirect to a new 'location'
cy.intercept('GET', '/users', (req) => {
// statusCode defaults to `302`
req.redirect('/customers', 301)
})
Ответ путем прослушивания событий (on):
cy.intercept('GET', '/users', (req) => {
req.on('before:response', (res) => {
// do something when the `before:response` event is triggered
})
})
cy.intercept('POST', '/users', (req) => {
req.on('response', (res) => {
// do something when the `response` event is triggered
})
})
См. пример для ограничения ответа См. больше примеров событий
Возврат Promise
Если из обратного вызова маршрута возвращается Promise, он будет ожидать его выполнения перед продолжением обработки запроса.
cy.intercept('POST', '/users', (req) => {
// asynchronously fetch test data
return getAuthToken().then((token) => {
// ...and apply it to the outgoing request
req.headers['Authorization'] = `Basic ${token}`
})
})
cy.intercept('POST', '/users', (req) => {
req.continue((res) => {
// the response will not be sent to the browser until
// `waitForSomething()` resolves:
return waitForSomething()
})
})
Создание заглушки ответа со строкой
// requests to create a user will be fulfilled
// with a body of 'success'
cy.intercept('POST', '/users', 'success')
// { body: 'sucess' }
Перехваченные запросы
Если в качестве обработчика cy.intercept() передана функция, она будет вызвана с первым аргументом, представляющим объект, который представляет перехваченный HTTP-запрос:
cy.intercept('/api', (req) => {
// `req` represents the intercepted HTTP request
})
Отсюда вы можете выполнить несколько действий с перехваченным запросом:
- вы можете изменить и проверить свойства запроса (тело, заголовки, URL, метод...)
- запрос может быть отправлен на реальный сервер верхнего уровня
- при желании вы можете перехватить ответ с него
- ответ может быть предоставлен для создания заглушки запроса
- можно прикрепить обработчики к различным событиям запроса
Свойства объекта запроса
Объект запроса (req ) имеет несколько свойств из самого HTTP-запроса. Все следующие свойства объекта req можно изменить, за исключением httpVersion:
{
/**
* The body of the request.
* If a JSON Content-Type was used and the body was valid JSON,
* this will be an object.
* If the body was binary content, this will be a buffer.
*/
body: string | object | any
/**
* The headers of the request.
*/
headers: { [key: string]: string }
/**
* Request HTTP method (GET, POST, ...).
*/
method: string
/**
* Request URL.
*/
url: string
/**
* URL query string as object.
*/
query: Record<string, string|number>
/**
* The HTTP version used in the request. Read only.
*/
httpVersion: string
}
Объект req также имеет некоторые дополнительные свойства, которые можно установить для управления поведением, специфичным для Cypress:
{
/**
* If provided, the number of milliseconds before an upstream
* response to this request will time out and cause an error.
* By default, `responseTimeout` from config is used.
*/
responseTimeout?: number
/**
* Set if redirects should be followed when this request is made.
* By default, requests will not follow redirects before
* yielding the response (the 3xx redirect is yielded).
*/
followRedirect?: boolean
/**
* If set, `cy.wait` can be used to await the request/response
* cycle to complete for this request via `cy.wait('@alias')`.
*/
alias?: string
}
Любые изменения свойств объекта req будут сохранены другими обработчиками запросов и, наконец, объединены в фактический исходящий HTTP-запрос.
Управление исходящим запросом с помощью req.continue()
Вызов req.continue() без аргументов приведет к отправке запроса, и ответ будет возвращен в браузер после вызова всех других обработчиков. Например, следующий код изменяет запрос POST и отправляет его на сервер верхнего уровня:
cy.intercept('POST', '/submitStory', (req) => {
req.body.storyName = 'some name'
// send the modified request and skip any other
// matching request handlers
req.continue()
})
Если в req.continue() передана функция, запрос будет отправлен на реальный сервер, и обработчик вызовет ответ, как только сервер полностью его получит. См. "Перехваченные ответы"
Примечание: вызов req.continue() остановит распространение запроса к следующему обработчику запроса в очереди. См. "Жизненный цикл перехвата" для получения дополнительной информации.
Предоставление заглушки ответа с помощью req.reply()
Функция req.reply() может использоваться для отправки заглушки ответа на перехваченный запрос. Передав строку, объект или StaticResponse в req.reply(), можно предотвратить достижение запроса целевого сервера.
Например, следующий код создаёт заглушку JSON-ответа из обработчика запросов:
cy.intercept('/billing', (req) => {
// dynamically get billing plan name at request-time
const planName = getPlanName()
// this object will automatically be JSON.stringified and
// sent as the response
req.reply({ plan: planName })
})
Вместо передачи простого объекта или строки в req.reply(), вы также можете передать StaticResponse объект. С помощью StaticResponse можно вызвать сетевую ошибку, замедлить/ускорить ответ, отправить фикстуру и многое другое.
Например, следующий код предоставляет динамически выбранную фикстуру с задержкой в 500 мс:
cy.intercept('/api/users/*', async (req) => {
// asynchronously retrieve fixture filename at request-time
const fixtureFilename = await getFixtureFilenameForUrl(req.url)
req.reply({
fixture: fixtureFilename,
delay: 500,
})
})
См. StaticResponse документацию для получения дополнительной информации о создании заглушек ответов таким образом.
Сокращенная форма req.reply()
req.reply() также поддерживает сокращённую форму, аналогичную res.send(), для избежания необходимости указывать объект StaticResponse.
// equivalent to `req.reply({ body })`
req.reply(body)
// equivalent to `req.reply({ body, headers })`
req.reply(body, headers)
// equivalent to `req.reply({ statusCode, body, headers})`
req.reply(statusCode, body, headers)
Функции для удобства
Также доступны две вспомогательные функции в req:
{
/**
* Destroy the request and respond with a network error.
*/
destroy(): void
/**
* Respond to this request with a redirect to a new 'location'.
* @param statusCode HTTP status code to redirect with. Default: 302
*/
redirect(location: string, statusCode?: number): void
}
См. примеры в разделе Управление ответом
Примечание: вызов req.reply() завершит фазу запроса и остановит распространение запроса к следующему соответствующему обработчику запроса в очереди. См. "Жизненный цикл перехвата" для получения дополнительной информации.
События запроса
Для расширенного использования доступны несколько событий в req, представляющие различные стадии жизненного цикла перехвата.
Вы можете подписаться на разные события, вызвав req.on:
cy.intercept('/shop', (req) => {
req.on('before:response', (res) => {
/**
* Emitted before `response` and before any `req.continue`
* handlers. Modifications to `res` will be applied to the
* incoming response. If a promise is returned, it will be
* awaited before processing other event handlers.
*/
})
req.on('response', (res) => {
/**
* Emitted after `before:response` and after any
* `req.continue` handlers - before the response is sent
* to the browser. Modifications to `res` will be applied
* to the incoming response. If a promise is returned, it
* will be awaited before processing other event handlers.
*/
})
req.on('after:response', (res) => {
/**
* Emitted once the response to a request has finished
* sending to the browser. Modifications to `res` have no
* impact. If a promise is returned, it will be awaited
* before processing other event handlers.
*/
})
})
См. "Перехваченные ответы" для получения дополнительной информации об объекте res , возвращаемом before:response и response. См. "Жизненный цикл перехвата" для получения дополнительной информации об упорядочении запросов.
Перехваченные ответы
Ответ можно перехватить двумя способами:
- передав обратный вызов в
req.continue()внутри обработчика запроса - подписавшись на события запросов
before:responseилиresponse(см. "События запроса")
Объект ответа, res, будет передан в качестве первого аргумента функции-обработчика:
cy.intercept('/url', (req) => {
req.on('before:response', (res) => {
// this will be called before any `req.continue` or
// `response` handlers
})
req.continue((res) => {
// this will be called after all `before:response`
// handlers and before any `response` handlers
// by calling `req.continue`, we signal that this
// request handler will be the last one, and that
// the request should be sent outgoing at this point.
// for that reason, there can only be one
// `req.continue` handler per request.
})
req.on('response', (res) => {
// this will be called after all `before:response`
// handlers and after the `req.continue` handler
// but before the response is sent to the browser
})
})
Свойства объекта ответа
Объект ответа (res) , переданный обработчикам ответа, имеет несколько свойств из самого HTTP-ответа. Все перечисленные ниже свойства объекта res могут быть изменены:
| Свойство | Описание |
|---|---|
| body | тело ответа (object, string, ArrayBuffer) |
| headers | заголовки ответа (object) |
| statusCode | код состояния ответа (number) |
| statusMessage | сообщение состояния ответа (string) |
Примечание о body: Если заголовок ответа содержит Content-Type: application/json и тело содержит допустимый JSON, это будет object. А если тело содержит двоичное содержимое, это будет буфер.
res также имеет некоторые необязательные свойства, которые можно установить для управления поведением, специфичным для Cypress:
| Свойство | Описание |
|---|---|
| throttleKbps | Максимальная скорость передачи данных ответа (килобит/секунда) |
| delay | Минимальная задержка сети или задержка, добавляемая к времени ответа (миллисекунды) |
Любые изменения в свойствах res сохранятся для других обработчиков ответа и, в конечном итоге, будут объединены с фактическим входящим HTTP-ответом.
Завершение ответа с помощью res.send()
Для завершения фазы ответа запроса вызовите res.send(). Необязательно, вы можете передать StaticResponse в res.send(), чтобы его объединить с фактическим ответом.
Когда res.send() вызывается, фаза ответа завершается немедленно, и другие обработчики ответа для текущего запроса не будут вызваны. Вот пример того, как можно использовать res.send():
cy.intercept('/notification', (req) => {
req.continue((res) => {
if (res.body.status === 'failed') {
// sends a fixture body instead of the existing 'res.body'
res.send({ fixture: 'success.json' })
}
})
})
См. StaticResponse документацию для получения дополнительной информации о формате.
Сокращенная форма res.send()
res.send() также поддерживает сокращенную форму, аналогичную req.reply(), чтобы не указывать объект StaticResponse:
// equivalent to `res.send({ body })`
res.send(body)
// equivalent to `res.send({ body, headers })`
res.send(body, headers)
// equivalent to `res.send({ statusCode, body, headers})`
res.send(statusCode, body, headers)
Функции для удобства
Также доступны две вспомогательные функции в res:
{
/**
* Wait for 'delay' milliseconds before sending the
* response to the client.
*/
setDelay: (delay: number) => IncomingHttpResponse
/**
* Serve the response at 'throttleKbps' kilobytes per second.
*/
setThrottle: (throttleKbps: number) => IncomingHttpResponse
}
Примечание: вызов res.send() завершит фазу ответа и остановит распространение ответа к следующему соответствующему обработчику ответа в очереди. См. "Жизненный цикл перехвата" для получения дополнительной информации.
Объекты StaticResponse
Объект StaticResponse представляет собой статически определённый ответ (заглушка).
Доступны следующие свойства в объекте StaticResponse:
| Вариант | Описание |
|---|---|
| statusCode | Код состояния HTTP-ответа |
| headers | Заголовки HTTP-ответа |
| body | Тело статического ответа (object, string, ArrayBuffer) (когда fixture опущено). |
| fixture | Отправка фикстуры в качестве тела HTTP-ответа (разрешено, когда body опущено). Читайте содержимое с кодировкой, отличной от по умолчанию для типа файла, передайте фикстуру как path,encoding. |
| forceNetworkError | Вызвать ошибку, прервав соединение браузера |
| delay | Минимальная задержка сети или задержка, добавляемая к времени ответа (миллисекунды) |
| throttleKbps | Максимальная скорость передачи данных ответа (килобит/секунда) |
Примечание: Все свойства необязательны.
Вы можете предоставить StaticResponse в Cypress тремя способами:
- Передать в
cy.intercept()какan argument, для создания заглушки ответа на маршрут:cy.intercept('/url', staticResponse) - Чтобы
req.reply(), для создания заглушки ответа от обработчика запроса:req.reply(staticResponse) - Чтобы
res.send(), для создания заглушки ответа от обработчика ответа:res.send(staticResponse)
См. "Создание заглушки ответа с помощью объекта StaticResponse" для примеров создания заглушек с помощью cy.intercept().
Жизненный цикл перехвата
Жизненный цикл перехвата cy.intercept() начинается, когда приложение отправляет HTTP-запрос, соответствующий одному или нескольким зарегистрированным маршрутам cy.intercept(). Далее каждый перехват имеет две фазы: запрос и ответ.
Маршруты cy.intercept() сопоставляются в обратном порядке определения, за исключением маршрутов, определённых с помощью { middleware: true }, которые всегда выполняются первыми. Это позволяет переопределить существующие объявления cy.intercept() путём определения перекрывающегося cy.intercept():
Фаза запроса
Следующие шаги используются для обработки фазы запроса.
- Начните с первой совпадающей маршрутизации в соответствии с вышеуказанным алгоритмом (сначала middleware, затем обработчики в обратном порядке).
- Была ли предоставлена обработка (тело,
StaticResponseили функция) дляcy.intercept()? Если нет, переходите к шагу 7. - Если обработчик был телом или
StaticResponse, немедленно завершите запрос с этим ответом. - Если обработчик был функцией, вызовите функцию с
req, входящим запросом в качестве первого аргумента. См. "Перехваченные запросы" для получения дополнительной информации об объектеreq.- Если вызывается
req.reply(), немедленно завершите фазу запроса с предоставленным ответом. См. "Предоставление заглушки ответа сreq.reply()". - Если вызывается
req.continue(), немедленно завершите фазу запроса и отправьте запрос на сервер назначения. Если дляreq.continue()предоставлен обратный вызов, он будет вызван во время фазы ответа
- Если вызывается
- Если обработчик вернул Promise, дождитесь разрешения Promise.
- Объедините все изменения в объекте запроса с реальным запросом.
- Если существует другой совпадающий
cy.intercept(), вернитесь к шагу 2 и продолжайте выполнять шаги с этим маршрутом. - Отправьте исходящий запрос на сервер назначения и завершите фазу запроса. Фаза ответа начаться сразу после получения ответа.
Фаза ответа
После получения HTTP-ответа от сервера upstream выполняются следующие шаги:
- Получите список зарегистрированных
before:responseобработчиков событий. - Для каждого
before:responseобработчика (если таковые имеются), вызовите его с объектомres.- Если вызывается
res.send(), завершите фазу ответа и объедините любые переданные аргументы с ответом. - Если возвращается Promise, дождитесь его. Объедините любые измененные свойства ответа с реальным ответом.
- Если вызывается
- Если для данного маршрута объявлен
req.continue()с обратным вызовом, вызовите обратный вызов с объектомres.- Если вызывается
res.send(), завершите фазу ответа и объедините любые переданные аргументы с ответом. - Если возвращается Promise, дождитесь его. Объедините любые измененные свойства ответа с реальным ответом.
- Если вызывается
- Получите список зарегистрированных
responseобработчиков событий. - Для каждого
responseобработчика (если таковые имеются), вызовите его с объектомres.- Если вызывается
res.send(), завершите фазу ответа и объедините любые переданные аргументы с ответом. - Если возвращается Promise, дождитесь его. Объедините любые измененные свойства ответа с реальным ответом.
- Если вызывается
- Отправьте ответ в браузер.
- После завершения ответа получите список зарегистрированных
after:responseобработчиков событий. - Для каждого
after:responseобработчика (если таковые имеются), вызовите его с объектомres(безres.send)- Если возвращается Promise, дождитесь его.
- Завершите фазу ответа.
Сопоставление URL-адресов с шаблонами Glob
При сопоставлении URL-адреса предоставление точного URL-адреса для сопоставления может быть слишком ограничительно. Например, что если вы хотите выполнить свои тесты на другом хосте?
// match any request that exactly matches the URL
cy.intercept('https://prod.cypress.io/users')
// matches this: https://prod.cypress.io/users
// ...but not this: https://staging.cypress.io/users
// ...or this: http://localhost/users
Сопоставление с шаблонами Glob обеспечивает необходимую гибкость:
cy.intercept('/users')
// matches all of these:
// https://prod.cypress.io/users
// https://staging.cypress.io/users
// http://localhost/users
cy.intercept('/users?_limit=+(3|5)')
// matches all of these:
// https://prod.cypress.io/users?_limit=3
// http://localhost/users?_limit=5
Cypress.minimatch
Внутри cy.intercept использует библиотеку minimatch с опцией { matchBase: true } для сопоставления по шаблонам Glob и предоставляет к ней доступ через глобальную переменную Cypress. Это позволяет вам протестировать свой шаблон в своем спецификации или в браузере консоли Test Runner.
Вы можете вызвать Cypress.minimatch с двумя аргументами — URL (string) и шаблоном (string) соответственно — и, если результат — true, то у вас совпадение!
expect(
Cypress.minimatch('http://localhost/users?_limit=3', '**/users?_limit=+(3|5)')
).to.be.true
expect(
Cypress.minimatch('http://localhost/users?_limit=5', '/users?_limit=+(3|5)', {
matchBase: true,
})
).to.be.true
expect(
Cypress.minimatch('http://localhost/users?_limit=7', '**/users?_limit=+(3|5)')
).to.be.false
Параметры minimatch
Вы также можете передать параметры (object) в качестве третьего аргумента, одним из которых является debug, который, если установлен в true, выведет подробный вывод, который может помочь вам понять, почему ваш шаблон не работает так, как вы ожидаете:
Cypress.minimatch('http://localhost/users?_limit=3', '**/users?_limit=+(3|5)', {
debug: true,
})
// true (plus debug messages)
Сравнение с cy.route()
В отличие от cy.route(), cy.intercept():
- может перехватывать все типы сетевых запросов, включая Fetch API, загрузки страниц, XMLHttpRequests, загрузки ресурсов и т. д.
- не требует вызова cy.server() перед использованием — на самом деле
cy.server()никак не влияет наcy.intercept(). - не имеет метода, установленного в
GETпо умолчанию, но перехватывает методы*.
cy.intercept() и кэширование запросов
cy.intercept() перехватывает запросы на уровне сети. Это может вызвать путаницу при попытке перехватить запрос, который уже был кэширован браузером. Если запрос обслуживается из кэша браузера, он никогда не достигнет уровня сети, и cy.intercept() никогда не сработает.
Чтобы увидеть, влияет ли это на ваше приложение, проверьте инструменты разработчика. В следующем примере все запросы, окруженные красной рамкой, были получены из кэша и не отправляют HTTP-запрос. Таким образом, они не могут быть перехвачены cy.intercept():
Если вы хотите перехватывать ресурсы, которые обычно отправляют заголовки кэша, вот некоторые обходные пути:
- Отключите заголовки кэша на сервере разработки в режиме тестирования.
- Отключить кэширование в ответах, добавив верхний
cy.intercept()для удаления заголовков кэша из необходимых запросов. Например:beforeEach(() => { cy.intercept( 'https://api.example.com/**/*', { middleware: true }, (req) => { req.on('before:response', (res) => { // force all API responses to not be cached res.headers['cache-control'] = 'no-store' }) } ) }) - Браузеры семейства Chromium только: Используйте
remote:debugger:protocolдля полного отключения кэша. Дополнительную информацию можно найти в этом комментарии к вопросу #14459
Журнал команд
cy.intercept('/accounts*').as('accountsGet')
cy.intercept('/company', { companyId: 1 }).as('companyGet')
cy.intercept('/teams*', [{ teamId: 2 }]).as('teamsGet')
Всякий раз, когда вы создаете cy.intercept() правила, Cypress отобразит новую панель инструментов под названием Маршруты. Она будет перечислять таблицу маршрутизации на панели инструментов, включая method, RouteMatcher, если маршрут заглушен, любые псевдонимы и количество сопоставленных запросов:
Когда выполняется запрос HTTP, Cypress регистрирует его в журнале команд и указывает, соответствует ли он cy.intercept() наличием желтого значка справа:
Круглая метка заполняется, если запрос отправлен на сервер назначения, но не заполнена, если запрос был заглушен с ответом.
Нажатие на запрос, который соответствует cy.intercept(), выведет дополнительную информацию о запросе и ответе в консоль:
Подробнее о регистрации запросов в Cypress.
История
| Версия | Изменения |
|---|---|
| 7.6.0 | Добавлена опция query к req (объект входящего запроса, переданный функциям обработчиков запросов).
|
| 7.0.0 | Удалена опция matchUrlAgainstPath из RouteMatcher, обратный порядок обработчиков, добавлены события запросов, удалено сопоставление по подстроке URL, удален псевдоним cy.route2, добавлена опция RouteMatcher middleware, переименовано res.delay() в res.setDelay() и res.throttle() в res.setThrottle(). |
| 6.4.0 | Переименовано свойство delayMs в delay (обратная совместимость).
|
| 6.2.0 | Добавлена опция matchUrlAgainstPath к RouteMatcher. |
| 6.0.0 | Переименовано cy.route2() в cy.intercept(). |
| 6.0.0 | Удалена опция experimentalNetworkStubbing и сделано стандартным поведением.
|
| 5.1.0 | Добавлена экспериментальная команда cy.route2() в опцию experimentalNetworkStubbing |
См. также
© 2017 Cypress.io
Licensed under the MIT License.
https://docs.cypress.io/api/commands/intercept