如果想從頭學起Cypress,可以看下面的系列文章哦
https://www.cnblogs.com/poloyy/category/1768839.html
作用
使用該命令在網路層管理 HTTP 請求的行為
包含以下功能
- 對任何型別的 HTTP 請求進行 stub 或 spy
- 在 HTTP 請求傳送到目標伺服器前,可以修改 HTTP 請求 body、headers、URL(類似抓包工具對請求進行打斷點然後修改)
- 動態或靜態地對 HTTP 請求的響應進行 stub
- 接收 HTTP 響應後可對 HTTP 響應 body、headers、status、code 進行修改(類似抓包工具對響應進行打斷點然後修改)
- 在所有階段都可以完全訪問所有 HTTP 請求
相較於 cy.route() 的不同
cy.route() 命令詳解:https://www.cnblogs.com/poloyy/p/13852941.html
- 可以攔截所有型別的網路請求,包括 Fetch API,頁面載入,XMLHttpRequest,資源載入等
- 不需要在使用前呼叫 cy.server() ,實際上 cy.server() 根本不影響 cy.intercept()
- 預設情況下沒有將請求方法設定為 GET
語法格式
cy.intercept(url, routeHandler?) cy.intercept(method, url, routeHandler?) cy.intercept(routeMatcher, routeHandler?)
url
要匹配的請求 URL ,可以是字串也可以是正規表示式
cy.intercept('http://example.com/widgets')
cy.intercept('http://example.com/widgets', { fixture: 'widgets.json' })
沒有指定請求方法的話,可以匹配任意型別的請求方法
method
請求方法
cy.intercept('POST', 'http://example.com/widgets', { statusCode: 200, body: 'it worked!' })
routeMatcher
- 它是一個物件
- 用於匹配此路由將處理哪些傳入的 HTTP 請求
- 所有物件屬性都是可選的,不是必填的
- 設定的所有屬性必須與路由匹配才能處理請求
- 如果將字串傳遞給任何屬性,則將使用 minimatch 將與請求進行全域性匹配
它有以下屬性
{ /** * 與 HTTP Basic身份驗證中使用的使用者名稱和密碼匹配 */ auth?: { username: string | RegExp, password: string | RegExp } /** * 與請求上的 HTTP Headers 匹配 */ headers?: { [name: string]: string | RegExp } /** * 與請求上的 hostname 匹配 */ hostname?: string | RegExp /** * If 'true', 只有 https 的請求會被匹配 * If 'false', 只有 http 的請求會被匹配 */ https?: boolean /** * 與請求上的 method 請求方法匹配 * 預設 '*', 匹配全部型別的 method */ method?: string | RegExp /** * 主機名後的路徑, 包括了 ? 後面的查詢引數 * www.baidu.com/s?wd=2 */ path?: string | RegExp /** * 和 path 一樣, 不過不管 ? 後面的查詢引數 * www.baidu.com/s */ pathname?: string | RegExp /** * 與指定的埠匹配, 或者傳遞多個埠組成的陣列, 其中一個匹配上就行了 */ port?: number | number[] /** * 與請求路徑 ? 後面跟的查詢引數匹配上 * wd=2 */ query?: { [key: string]: string | RegExp } /** * 完整的請求 url * http://www.baidu.com/s?wd=2 */ url?: string | RegExp }
routeHandler
- routeHandler 定義瞭如果請求和 routeMatcher 匹配將對請求進行的指定的處理
- 可接受的資料型別:string、object、Function、StaticResponse
StaticResponse
- 相當於一個自定義響應體物件
- 可以自定義 Response headers、HTTP 狀態碼、Response body 等
- 詳細栗子將在後面展開講解
StaticResponse 物件的屬性
{ /** * 將 fixture 檔案作為響應主體, 以 cypress/fixtures 為根目錄 */ fixture?: string /** * 將字串或 JSON 物件作為響應主體 */ body?: string | object | object[] /** * 響應 headers * @default {} */ headers?: { [key: string]: string } /** * 響應狀態碼 * @default 200 */ statusCode?: number /** * 如果 true, Cypress 將破壞網路連線, 並且不傳送任何響應 * 主要用於模擬無法訪問的伺服器 * 請勿與其他選項結合使用 */ forceNetworkError?: boolean /** * 傳送響應前要延遲的毫秒數 */ delayMs?: number /** * 以多少 kbps 傳送響應體 */ throttleKbps?: number }
string
- 如果傳遞一個字串,這個值相當於響應 body 的值
- 等價於 StaticResponse 物件 { body: "foo" }
object
- 如果傳遞了沒有 StaticResponse 金鑰的物件,則它將作為 JSON 響應 Body 傳送
- 例如, {foo:'bar'} 等價於 StaticResponse 物件 {body:{foo:'bar'}}
function
- 如果傳遞了一個回撥函式,當一個請求匹配上了該路由將會自動呼叫這個函式
- 函式第一個引數是請求物件
- 在回撥函式內部,可以修改外發請求、傳送響應、訪問實際響應
- 詳細栗子將在後面展開講解
命令返回結果
- 返回 null
- 可以連結 as() 進行別名,但不可連結其他命令
- 可以使用 cy.wait() 等待 cy.intercept() 路由匹配上請求,這將會產生一個物件,包含匹配上的請求/響應相關資訊
實際栗子的前置準備
Cypress 官方專案的下載地址:https://github.com/cypress-io/cypress-example-kitchensink
下載好後進入下圖專案資料夾
啟動專案
npm start
通過 URL 路由匹配請求的栗子
測試程式碼
等價於 route() 的測試程式碼
注: route() 未來將會被棄用
執行結果
登入請求匹配上了路由
Console 檢視 cy.wait() 返回的物件
最重要的當然是 request 和 response 兩個屬性
通過 RouteMatcher 路由匹配請求的栗子
測試程式碼
斷言請求體和響應狀態碼
執行結果
Console 檢視 cy.wait() 返回的物件
另一種斷言方式
// 斷言匹配此路由的請求接收到包含【username】的請求 body cy.wait('@login3').its('request.body').should('have.property', 'username') // 斷言匹配此路由的請求接收到 HTTP 狀態碼為 500 cy.wait('@login3').its('response.statusCode').should('eq', 200) // 斷言匹配此路由的請求接收到包含【redirect】的請求 body cy.wait('@login3').its('response.body').should('have.property', 'redirect')
不過這樣的話只能每次寫一條不能同時三條都寫,所以還是建議像程式碼圖一樣,先 .then() 再進行斷言
自定義不同型別的響應體的各種栗子
自定義一個純字串的響應體
測試程式碼
執行結果
介面響應
自定義一個 JSON 的響應體
測試程式碼
會從cypress安裝目錄/fixtures 下讀取對應的資料檔案,它會變成響應 body 的資料
test.json 資料檔案
執行結果
介面響應
自定義一個 StaticResponse 的響應體
測試程式碼
自定義了響應body、statusCode,還有返回響應的延時時間
執行結果
延時生效了
body 和 statusCode 變成自定義的資料了
攔截請求的栗子
前置操作
beforeEach(() => { cy.visit('http://localhost:7079/login') })
斷言請求的栗子
測試程式碼
執行結果
Console 檢視列印結果
可以看到回撥函式只有一個引數,就是 request 引數
重點
回撥函式內不能包含 cy.**() 的命令,如果包含會報錯
簡單來說就是
cy.type() 命令執行完後會返回一個 promise 物件,同時又會呼叫回撥函式,而回撥函式內又呼叫了 cy.get() 返回了一個 promise 物件,Cypress 會將這種情況當做測試失敗處理
將請求傳遞給下一個路由處理程式
前言
意思就是一個請求可以同時匹配上多個路由
測試程式碼
執行結果
一個登入請求匹配成功了兩個路由,且回撥函式會按匹配的順序執行
總結
回撥函式的引數就是一個請求物件,它其實可以呼叫以下方法
{ /** * 銷燬該請求並返回網路錯誤的響應 */ destroy(): void /** * 控制請求的響應 * 如果傳入的是一個函式, 則它是回撥函式, 當響應時會呼叫 * 如果傳入的是一個 StaticResponse 物件, 將不會發出請求, 而是直接將這個物件當做響應返回 */ reply(interceptor?: StaticResponse | HttpResponseInterceptor): void /** * 使用 response body(必填) 和 response header(可選) 響應請求 */ reply(body: string | object, headers?: { [key: string]: string }): void /** * 使用 HTTP 狀態碼(必填)、 response body(可選)、response header(可選) 響應請求 */ reply(status: number, body?: string | object, headers?: { [key: string]: string }): void /** * 重定向到新的 location 來響應請求, * @param statusCode 用來重定向的 HTTP 狀態程式碼, Default: 302 */ redirect(location: string, statusCode?: number): void }
攔截響應的栗子
req.reply() 函式詳解
前言
可以使用 req.reply() 函式來動態控制對請求的響應
使用講解
cy.intercept('/login', (req) => { // functions on 'req' can be used to dynamically respond to a request here // 將請求傳送到目標伺服器 req.reply() // 將這個 JSON 物件響應請求 req.reply({plan: 'starter'}) // 將請求傳送到目標伺服器, 並且攔截伺服器返回的實際響應, 然後進行後續操作(類似抓包工具對響應打斷點) req.reply((res) => { // res 就是實際的響應物件 }) })
.reply() 直接修改響應的栗子
測試程式碼
介面響應內容
攔截響應的小栗子
測試程式碼
執行結果
Console 檢視列印結果
一個是 request 物件,一個是 response 物件
自定義響應內容
前言
- 可以使用 resp.send() 函式動態控制傳入的響應
- 另外,當響應傳送到瀏覽器時,對 resp 的任何修改都將保留
- 如果尚未呼叫 resp.send() ,則它會在 req.reply() 回撥函式完成後隱式呼叫
使用講解
cy.intercept('/notification', (req) => { req.reply((resp) => { // Success 將作為 response body 返回到瀏覽器 resp.send('Success') // 將 success.json 裡面的資料作為 response body 返回到瀏覽器 resp.send({fixture: 'success.json'}) // 將響應延遲 1000ms resp.delay(1000) // 將響應限制為 64kbps resp.throttle(64) }) })
傳遞字串作為響應內容
測試程式碼
介面響應內容
傳遞 JSON 物件作為響應內容
測試程式碼
介面響應內容
傳遞 StaticResponse 物件作為響應內容
測試程式碼
介面響應內容
resp 可呼叫的函式總結
{ /** * 可以自定義 response statusCode、response body、response header * 也可以直接傳 StaticResponse 物件 */ send(status: number, body?: string | number | object, headers?: { [key: string]: string }): void send(body: string | object, headers?: { [key: string]: string }): void send(staticResponse: StaticResponse): void /** * 繼續返回響應 */ send(): void /** * 等待 delayMs 毫秒,然後再將響應傳送給客戶端 */ delay: (delayMs: number) => IncomingHttpResponse /** * 以多少 kbps 的速度傳送響應 */ throttle: (throttleKbps: number) => IncomingHttpResponse }