cypress
下載與安裝
安裝方式一
- 安裝node.js
- 因為npm直接下載會也很慢,所以先修改下載源
- 執行命令 npm config set registry http://registry.npm.taobao.org
- 查看是否更改成功 npm config get registry
- 本地創建一個名為cypresses的目錄
- 在該目錄下執行 npm install cypress --save-dev
- .bin目錄下啟動cypress cypress open
- 啟動過后會發現缺少文件 npm WARN saveError ENOENT: no such file or directory, open 'D:\Cypress\package.json'
- 在新建的目錄下創建package.json,(如果有直接添加如下代碼)
- 根目錄下執行命令 npm run cypress:open
{
"scripts": {
"cypress:open": "cypress open"
}
}
安裝方式二
執行命令 yarn add cypress --dev
基本介紹
簡介
- 基於JS,可以對瀏覽器中運行的任何內容進行快速、簡單、可靠的測試
- Cypress是自集成的,提供了一套完整的端到端測試,無須借助其他外部工具,安裝后即可快速地創建、編寫、運行測試用例,支持回放。
- Cypress允許編寫所有類型的測試,覆蓋了測試金字塔模型的所有測試類型【界面測試,集成測試,單元測試】
- Cypress 底層協議不采用 WebDriver
原理
- 大多數測試工具是通過外部瀏覽器運行,並在網絡上執行遠程命令來運行,主要是因為webdriver底層通信協議基於JSON Wire Protocol,運行需要網絡通信。
- 但是cypress和webdriver的方式完全相反,它與應用程序在相同的聲明周期里執行。
cypress運行更快的原因
- cypress測試代碼和應用程序均運行在由cypress全權控制的瀏覽器中
- 且它們運行在同一個Domain下的不同iframe中,所以cypress的測試代碼可以直接操作DOM、window objects、local storages而無需通過網絡訪問
cypress穩定性、可靠性更高的原因
- cypress可以在網絡層進行及時讀取和更改網絡流量的操作
- cypress背后是node.js process控制的proxy進行轉發,使得cypress不僅可以修改進出瀏覽器的所有內容,還可以更改可能影響自動化操作的代碼。
- cypress相對於其他測試工具來說,能從根本上控制整個自動化測試的流程。
cypress的特性
- cypress在測試代碼運行時會自動拍照,也就是等測試結束之后,用戶可以在cypress提供的test runner,通過懸停在命令上的方式看運行時的步驟。
- 實時重新加載,當測試代碼修改保存后,cypress會自動加載改動的地方,重新運行測試。
- 可調試性,當測試失敗時,可以直接通過開發者工具進行調試。
- 自動等待,使用cypress,就無需與selenium一樣在測試中添加強制、顯示等待以及隱式等待了,cypress會自動等待元素到可靠操作狀態時才執行命令或斷言。
- 截圖和視頻,cypress在測試運行失敗時會自動截圖,在無頭運行時,會錄制整個測試套件的視頻。
解析Cypress的默認文件結構
fixture測試夾具
簡介
- 測試夾具通常配合cy.fixture()使用
- 主要用來存儲測試用例的外部靜態數據
- fixtures默認就在cypress/fixtures目錄下,單也可以配置到另一個目錄
外部靜態數據的詳解
- 測試夾具的靜態數據通常存儲在.json文件中,如自動生成的examples.json
- 靜態數據通常是某個網絡請求對應的響應部分,包括HTTP狀態碼和返回值,一般是復制過來更改而不是自己手工填寫.
- 如果你的測試需要對某些外部接口進行訪問並依賴它的返回值,則可以使用測試夾具而無須真正訪問這個接口(用過mock的能有體會)
使用測試夾具的好處
- 消除了對外部功能模塊的依賴.
- 已編寫的測試用例可以使用測試夾具提供的固定返回值,並且你確切知道這個返回值是你想要的.
- 因為無需真正的發送網絡請求,所以測試更快.
test file 測試文件
- 測試文件就是測試用例,默認位於cypress/integration,但也可以配置到另一個目錄
- 所有在integration文件下,且文件格式是以下的文件都將被Cypress識別為測試文件.
- 創建好后,Cypress的Test Runner刷新就能看到對應測試文件
plugin file 插件文件
前言
- Cypress的優點就是在運行是在瀏覽器之內,使得Cypress跟其他的測試框架相比,有顯著的架構優勢
- 雖然提供了可靠性測試,但也使在瀏覽器之外進行通信更加困難
插件文件的誕生與應用
- 為了解決跟外部通信,可以修改或擴展Cypress的內部行為(如:動態修改配置信息和環境變量等),也可以自定義自己的插件.
- 默認情況,插件位於cypress/plugins/index.js中,單可以配置到另一個目錄
- 為了方便,每個測試文件運行之前,Cypress都會自動加載插件文件
- 我們一般通過動態修改來自cypress.json、cypress.env.json,CLI或系統環境變量的已解析配置和環境變量以及修改特定瀏覽器的啟動參數、將消息直接從測試代碼傳遞到后端
support file 支持文件
簡介
- 支持文件目錄防止可重用配置項,如底層通用函數或全局默認配置
- 支持文件默認位於cypress/support/index.js中,單可以配置到另一個目錄
- 為了方便,每個測試文件運行之前,Cypress都會自動加載支持文件
使用
只需要在cypress/support/index.js文件添加beforeEach()函數即可,這將實現每次測試運行前能打印出所有的環境給變量信息.如下
beforeEach(function () {
cy.log(`當前環境變量為${JSON.stringify(Cypress.env())}`)
cy.log(`當前配置項信息為${JSON.stringify(Cypress.config())}`)
})
自定義Cypress
前言
- Cypress不僅支持用戶自定義文件結構,還支持用戶自定義Cypress的各項配置
- Cypress可以通過cypress.json文件來實現各項配置的自定義(默認文件是空的)
全局配置項
配置項 | 默認值 | 描述 |
---|---|---|
baseUrl | null | url前綴,cy.visit()或cy.request()命令經常會用,它的值通常被設置為系統主域名 |
env | {} | 任何想用做環境變量的變量都可以設置在env中 |
ignoreTestFiles | *.hot-update.js | 忽略某些測試用例:被此項規則匹配的測試用例不會被執行,建議使用http://globtester.com來測試哪些文件匹配 |
numTestsKeptLnMemory | 50 | 保留在內存中的測試用例(主要是:快照和命令數據)的數量,如果在測試運行期間瀏覽器的內存消耗很高,請減少這個數字 |
port | null | Cypress占用的端口,默認隨機生成 |
reporter | spec | Cypress運行期間使用哪個reporter,有Mocha內置的reporter、teamcity、junit等 |
reporterOptions | null | reporter支持的選項配置 |
testFiles | **/*.* | 要加載的測試文件,可以指定具體文件,也可以模糊匹配 |
watchForFileChanges | true | Cypress在運行中自動檢測文件變化,當有變化時,自動重新運行受影響的測試用例(建議打開) |
超時Timeouts相關
- 超時是必須要了解的核心概念
- 幾乎所有命令都可以某種方式超時
- 所有斷言,無論它們是默認斷言還是自己添加的斷言都具有相同的超時時間
配置項 | 默認值 | 描述 |
---|---|---|
defaultCommandTimeout | 4000 | 命令默認超時時間,ms為單位 |
execTimeout | 60000 | cy.exec()命令期間,等待系統命令完成執行的超時時間 |
taskTimeout | 60000 | cy.task()命令期間,等待系統命令完成執行的超時時間 |
pageLoadTimeout | 60000 | 等待頁面加載或cy.visit()、cy.go()、cy.reload()命令來觸發它們的頁面加載時間的時間 |
requestTimeout | 5000 | 等待cy.wait()命令中的XHR請求發出的超時時間 |
responseTimeout | 30000 | 如下命令的響應超時時間cy.request()、cy.wait()、cy.fixture()、cy.getCookie()、cy.getCookies()、cy.setCookie()、cy.clearCookie()、cy.clearCookies()、cy.screenshot() |
文件夾/文件相關
相對於默認文件結構來說,Cypress支持用戶自定義的文件結構
配置項 | 默認值 | 描述 |
---|---|---|
fileServerFolder | 項目根目錄 | fileserver目錄 |
fixturesFolder | cypress/fixtures | 測試夾具默認文件夾,可更改默認值為false來禁用它 |
integrationFolder | cypress/integration | 測試用例默認文件夾 |
pluginsFile | cypress/plugins/index.js | 插件文件默認文件夾 |
screenshotsFolder | cypress/screenshots | 由測試失敗或cy.screenshot()命令引發的截圖,截圖默認文件夾 |
supportFile | cypress/support/index.js | 測試加載之前要加載的路徑 |
Cypress.config()
除了直接在cypress.json文件里更改配置項之外,Cypress還允許我們通過Cypress.config()去獲取或覆蓋某些配置項,語法如下:
// 獲取所有config信息
Cypress.config()
// 獲取指定配置項的信息
Cypress.config(name)
// 更改指定配置項的默認值
Cypress.config(name, value)
// 使用對象字面量(object literal)設置多個配置項
Cypress.config(object)
Cypress的重試機制
重試是Cypress的核心概念之一,有助於我們寫出更加健壯的測試。
問題前言
現在的web應用基本都是異步的,出現一下的情況就要做重試的操作
- 如果斷言發生時,應用程序尚未更新DOM怎么辦?
- 如果斷言發生時,應用程序正在等待其后端響應,而導致頁面暫無結果怎么辦?
- 如果斷言發生時,應用程序正在進行密集計算,而導致頁面未及時更新怎么辦?
上述情況在測試中經常會發生,一般處理方法是在斷言前增加斷言時間(或像selenium一樣中使用顯式或者隱式等待),不過仍然會出現測試再次失敗的情況。、
Cypress如何處理問題前言
- cy.get()命令之后的斷言通過,則該命令成功執行完成。
- cy.get()命令之后的斷言失敗,則cy.get()命令會自動重新查詢web應用程序的DOM樹,然后Cypress將再次嘗試對cy.get()返回的元素進行斷言。
- 如果斷言仍然失敗,cy.get()仍然會重新查詢DOM樹,以此類推
- 直到斷言成功或cy.get()命令超時
Cypress的處理機制有點像selenium的顯示等待,只不過Cypress是全局的,不用針對元素去單獨識別,Cypress這種自動重試機制避免了在測試代碼中編寫硬編碼等待(強制等待),使測試代碼更加健壯。
多重斷言
- 在日常測試匯總,有時候需要多重斷言,即獲取元素后跟多個斷言。
- 在多重斷言中,Cypress將按順序進行斷言,也就是當第一個斷言通過后,會進行第二個斷言,通過后進行第三個斷言,依次類推。
重試機制的條件
- Cypress並不會重試所有的命令,當命令可能改變被測應用程序的狀態時,該命令將不會重試(比如點擊輸入等)
- Cypress僅會重試那些查詢DOM的命令:cy.get()、find()、contains()等。
斷言的相關實例
context('登錄測試', function () {
const username = 'wupeng'
const password = 'bb961202'
beforeEach(function () {
cy.visit('http://192.168.102.210:10001/zentao/user-login.html')
})
it('登錄成功,跳轉到首頁', function () {
cy.get('input[name=account]').type(username)
cy.get('input[name=password]').type(password)
// 驗證input下的屬性type,值是否為password
cy.get('input[name=password]').should('have.attr','type','password')
cy.get('#submit').click()
// 驗證url中是否包含/my
cy.url().should('include','/my')
});
})
Mocha介紹
前言
- Cypress底層依賴於很多優秀的開源框架,其中就有Mocha
- Mocha是一個適用於Node.js和瀏覽器的測試框架,它使得異步測試變的簡單。
JS語言帶來的問題
JS是單線程異步執行的,這使得測試變的復雜,因為無法像測試同步執行的代碼那樣,直接判斷函數的返回值是否符合預期(因為給函數賦值時函數可能並未執行)
如何驗證異步函數的正確性
- 需要測試框架愛支持回調Promise或者其他方式來驗證異步函數的正確性。
- Mocha提供了出色的異步支持報貨Promise,從而使得異步測試變得簡單。
Mocha提供了什么
- 多種接口來定義測試套件,Hooks,單個測試
- BDD,行為驅動開發
- TDD,測試驅動開發
- Exports、QUnit、Require
常見Mocha模塊
- describe()
- context()
- it()
- before()
- beforEach()
- afterEach()
- after()
- .only()
- .skip()
必要的組成部分
- 對於每一條可執行的測試用例,有兩個必要的組成部分,describe()代表測試套件,里面可以設定context(),也可以包括多個測試用例it(),還能嵌套子測試套件
- 一個測試條件可以不包括任何鈎子函數,但必須要包含至少一條測試用例it()
- context()是describe()的別名,行為方式是一致的,可以直接使用context()代替describe()
鈎子函數Hook
Mocha提供的Hook
- before()
- 該測試套件下,所有測試用例的統一前置操作
- 它在一個describe()或context()內只會執行一次,在所有it()之前都會執行
- beforeEach()
- 該測試套件下,每個測試用力的前置操作
- 一個describe()或contest()內有多少個測試用例it(),就會執行幾次beforeEach()
- afterEach()
- 該測試套件下,每個測試用例的后置操作
- 一個describe()或context()內有多少個測試用例it(),就會執行幾次afterEach()
- after()
1. 該測試套件下,所有測試用例的統一后置操作
2. 在一個describe()或context()內只會執行一次,在所有it()之前執行。
代碼實例
describe('hook', function () {
before(function () {
cy.log("所有測試用例之前的操作,只會執行一次")
});
beforeEach(function () {
cy.log("所有測試用例之前的操作,每個it之前執行一次")
});
after(function () {
cy.log("所有測試用例之后的操作,只會執行一次")
});
afterEach(function () {
cy.log("所有測試用例之后的操作,每個it之后都會執行一次")
});
it('打印數字1', function () {
cy.log("1")
});
it('打印數字2', function () {
cy.log("2")
});
});
hook的作用
利用鈎子函數可以在所有測試用例執行前做一些預置操作,或者在測試結束后做一些后置操作
跳過執行與指定執行
- 在做自動化測試中,跳過執行某些測試用例
- 通過.skip()可以完成跳過執行測試套件或測試用例
- 通過describe.skip()或者context.skip()來跳過不需要執行的測試套件
- 通過it.skip()來跳過不需要執行的測試用例
- 同樣也可以只運行某些指定的測試用例
- 通過.only()可以完成指定執行測試套件或測試用例,當存在.only()指定某個測試套件或測試用例時,只有這個測試套件或測試用例會被執行,其他未加.only()的測試套件或測試用例都不會執行
- 通過describe.only()或者context.only()來指定需要執行的測試套件(不過注意的是即使添加了.only()的子套件,即使父套件沒有添加,它也會執行。添加了.only()的套件,該套件下的所有測試用例默認都會執行)
- 通過it.only()來指定需要執行的測試用例(如果當前測試套件下有it.only(),那么即使存在測試套件添加了.only(),該測試套件也不會執行。如果同個測試套件下有多個it.only()時,都會執行)
- 通過Cypress.env('flag')==1來實現動態跳過測試用例,最后重啟cypress,啟動命令:npm run cypress:open --env flag=1
// 實例代碼
describe('測試跳過', function () {
beforeEach(function () {
cy.log("前置操作")
});
it('測試開始1', function () {
if (Cypress.env('flag')===1){
cy.log("執行該用例")
}else{
cy.log("跳過執行")
this.skip()
}
});
it('測試開始2', function () {
cy.log("測試成功")
});
});
this.skip(),當測試用例內調用該方法時,方法后面的代碼都不會執行,方法前面若是調用Cypress的方法則也不會執行。
只有非Cypress方法才會執行,如console.log("log")。
動態生成測試用例
前言
- 自動化測試中,數據驅動是很重要的一個點
- 實際項目中,肯定會出現這種情況:多條測試用例的執行步驟,斷言步驟完全一致,只有輸入和輸出數據不一樣
- 這個時候依靠數據驅動(數據參數化)來解決這個問題可以提升我們的測試效率
- 在 Cypress,可以通過數據來動態生成測試用例,以達到數據驅動的效果
實際操作步驟
- 新建一個數據文件,存放輸入數據。在Cypress安裝目錄/cypress/integration文件下,創建一個目錄data,在該目錄下創建一個test.data.js文件(名字隨意取)。
- 創建測試文件
- 執行
代碼實例
// 新建數據文件
export const testLoginUser=[
{
summary:"登錄成功",
username:"admin",
password:"123456"
},
{
summary:"登錄失敗",
username:"admin",
password:"1234567"
}
]
// 新建測試文件
import {testLoginUser} from "./data/test.data";
describe('登錄', function () {
beforeEach(function () {
cy.visit("http://192.168.102.49:5001/#/")
});
// 循環測試數據
for (const user of testLoginUser){
it(user.summary, function () {
cy.get("#username").type(user.username)
cy.get("#password").type(user.password)
cy.get("button[type=submit]").click()
cy.url().should('include','bay/stationManage')
});
}
});
斷言
Cypress的斷言基於Chai斷言庫,並且增加了對Sinon-Chai、Chai-jQuery斷言庫的支持,其中就包括BDD和TDD格式的斷言。
長度
// 重試,直至找到3個匹配的<li.selected>
cy.get('li.selected').should('have.length',3)
類
// 重試,直至這個input不再有disabled的class
cy.get('form').find('input').should('not.hava.class','disabled')
值
// 重試,直至這個textarea的值為 poloyy
cy.get('textarea').should('have.value','poloyy')
文本內容
// 重試,直至這個span不再包含'click me'
cy.get('a').parent('span.help').should('not.contain','click me')
元素是否可見
// 重試,直至button可見
cy.get('button').should('be.visible')
元素是否存在
// 重試,直至 id=loading 元素不再存在
cy.get('#loading').should('not.exist')
針對元素狀態
// 重試,直至radio狀態是checked
cy.get(':radio').should('be.checked')
針對元素屬性值
// 重試,驗證元素屬性值是否存在
cy.get('#kw').should('hava.attr','username','wupeng')
針對CSS
// 重試,直至complete這個類有匹配的css為止
cy.get('.completed').should('have.css','text-decoration','line-through')
環境變量
baseUrl
- 通過環境變量設置測試套件訪問的URL,這只是其中的一種方式
- Cypress可以通過配置baseUrl取代環境變量的方式,當配置了baseUrl,測試套件中的cy.visit()、cy.request()都會自動以baseUrl的值作為前綴
- 並且,當需要訪問某些網址或者發起接口請求時,在代碼中就可以不用在指定請求的host或者url
在cypress.json文件進行配置就可以,如下
{
"baseUrl": "http://localhost:7077"
}
如何調用
需要注意的是:在調用visit或request時,不管如何都需要穿個空的字符串
cy.visit("")
通過環境變量覆蓋baseUrl
即使配置了baseUrl,也可以通過環境變量來覆蓋它
CYPRESS_baseUrl=https://staging.app.com cypress run
設置環境變量的幾種方式
最常見的做法就是在測試運行時,使用Cypress.env()訪問環境變量的值
cypress.json中設置
在cypress.json的env鍵下設置的任何key:value都是環境變量
cypress.json代碼
{
"baseUrl": "http://localhost:7077",
"env": {
"foor": "bar",
"key": "value"
}
}
測試代碼
// 獲取所有環境變量
Cypress.env()
// 獲取某個環境變量的值
Cypress.env("foor")
創建cypress.env.json文件
- 創建自己的cypress.env.json文件,Cypress將會自動檢查它
- 里面的值會覆蓋cypress.json中重名的環境變量
- 創建與cypress.json統計目錄下
環境變量操作實例
// 測試用例代碼
describe('測試百度', function () {
context('搜索功能', function () {
beforeEach(function () {
cy.visit("")
});
it('搜索QQ ', function () {
cy.get(Cypress.env("name")).type("qq").should('have.value', 'qq')
});
it('搜索谷歌', function () {
cy.get('#kw').type('谷歌').should('have.value', '谷歌')
Cypress.config()
});
})
});
// 第一種設置變量的方式 在cypress.json添加name的參數("env"必須要加上)
{
"baseUrl": "https://www.baidu.com",
"env": {
"name": "#kw"
}
}
// 第二種設置變量的方式 根目錄下創建cypress.env.json,添加name的參數
{
"name": "#kw"
}
元素定位選擇器
對於難以使用普通方式定位的元素,提供了以下兩種選擇器
Cypress.$('#main2')
// 等價於
cy.get('#main2')
常規CSS選擇器
選擇器 | 例子 | 例子描述 | CSS |
---|---|---|---|
.class | .intro | 選擇 class="intro" 的所有元素。 | 1 |
#id | #firstname | 選擇 id="firstname" 的所有元素。 | 1 |
* | * | 選擇所有元素。 | 2 |
element | p | 選擇所有 元素。 |
1 |
element,element | div,p | 選擇所有
元素和所有
元素。 |
1 |
element element | div p | 選擇
元素內部的所有
元素。 |
1 |
element>element | div>p | 選擇父元素為
元素的所有
元素。 |
2 |
element+element | div+p | 選擇緊接在
元素之后的所有
元素。 |
2 |
[attribute] | [target] | 選擇帶有 target 屬性所有元素。 | 2 |
[attribute=value] | [target=_blank] | 選擇 target="_blank" 的所有元素。 | 2 |
[attribute~=value] | [title~=flower] | 選擇 title 屬性包含單詞 "flower" 的所有元素。 | 2 |
[attribute|=value] | [lang|=en] | 選擇 lang 屬性值以 "en" 開頭的所有元素。 | 2 |
:link | a:link | 選擇所有未被訪問的鏈接。 | 1 |
:visited | a:visited | 選擇所有已被訪問的鏈接。 | 1 |
:active | a:active | 選擇活動鏈接。 | 1 |
:hover | a:hover | 選擇鼠標指針位於其上的鏈接。 | 1 |
:focus | input:focus | 選擇獲得焦點的 input 元素。 | 2 |
:first-letter | p:first-letter | 選擇每個 元素的首字母。 |
1 |
:first-line | p:first-line | 選擇每個 元素的首行。 |
1 |
:first-child | p:first-child | 選擇屬於父元素的第一個子元素的每個 元素。 |
2 |
:before | p:before | 在每個 元素的內容之前插入內容。 |
2 |
:after | p:after | 在每個 元素的內容之后插入內容。 |
2 |
:lang(language) | p:lang(it) | 選擇帶有以 "it" 開頭的 lang 屬性值的每個 元素。 |
2 |
element1~element2 | p~ul | 選擇前面有 元素的每個
|
3 |
[attribute^=value] | a[src^="https"] | 選擇其 src 屬性值以 "https" 開頭的每個 元素。 | 3 |
[attribute$=value] | a[src$=".pdf"] | 選擇其 src 屬性以 ".pdf" 結尾的所有 元素。 | 3 |
[attribute**=value*] | a[src*="abc"] | 選擇其 src 屬性中包含 "abc" 子串的每個 元素。 | 3 |
:first-of-type | p:first-of-type | 選擇屬於其父元素的首個 元素的每個 元素。 |
3 |
:last-of-type | p:last-of-type | 選擇屬於其父元素的最后 元素的每個 元素。 |
3 |
:only-of-type | p:only-of-type | 選擇屬於其父元素唯一的 元素的每個 元素。 |
3 |
:only-child | p:only-child | 選擇屬於其父元素的唯一子元素的每個 元素。 |
3 |
:nth-child(n) | p:nth-child(2) | 選擇屬於其父元素的第二個子元素的每個 元素。 |
3 |
:nth-last-child(n) | p:nth-last-child(2) | 同上,從最后一個子元素開始計數。 | 3 |
:nth-of-type(n) | p:nth-of-type(2) | 選擇屬於其父元素第二個 元素的每個 元素。 |
3 |
:nth-last-of-type(n) | p:nth-last-of-type(2) | 同上,但是從最后一個子元素開始計數。 | 3 |
:last-child | p:last-child | 選擇屬於其父元素最后一個子元素每個 元素。 |
3 |
:root | :root | 選擇文檔的根元素。 | 3 |
:empty | p:empty | 選擇沒有子元素的每個 元素(包括文本節點)。 |
3 |
:target | #news:target | 選擇當前活動的 #news 元素。 | 3 |
:enabled | input:enabled | 選擇每個啟用的 input 元素。 | 3 |
:disabled | input:disabled | 選擇每個禁用的 input 元素 | 3 |
:checked | input:checked | 選擇每個被選中的 input 元素。 | 3 |
:not(selector) | :not(p) | 選擇非 元素的每個元素。 |
3 |
::selection | ::selection | 選擇被用戶選取的元素部分。 | 3 |
jQuery選擇器
元素 元素選擇器 | 實例 | 選取 |
---|---|---|
* | $("*") | 所有元素 |
#id | $("#lastname") | id="lastname" 的元素 |
.class | $(".intro") | 所有 class="intro" 的元素 |
element | $("p") | 所有 元素 |
.class.class | $(".intro.demo") | 所有 class="intro" 且 class="demo" 的元素 |
:first | $("p:first") | 第一個 元素 |
:last | $("p:last") | 最后一個 元素 |
:even | $("tr:even") | 所有偶數 |
:odd | $("tr:odd") | 所有奇數 |
:eq(index) | $("ul li:eq(3)") | 列表中的第四個元素(index 從 0 開始) |
:gt(no) | $("ul li:gt(3)") | 列出 index 大於 3 的元素 |
:lt(no) | $("ul li:lt(3)") | 列出 index 小於 3 的元素 |
:not(selector) | $("input:not(:empty)") | 所有不為空的 input 元素 |
:header | $(":header") | 所有標題元素 - |
:animated | 所有動畫元素 | |
:contains(text) | $(":contains('W3School')") | 包含指定字符串的所有元素 |
:empty | $(":empty") | 無子(元素)節點的所有元素 |
:hidden | $("p:hidden") | 所有隱藏的 元素 |
:visible | $("table:visible") | 所有可見的表格 |
s1,s2,s3 | $("th,td,.intro") | 所有帶有匹配選擇的元素 |
[attribute] | $("[href]") | 所有帶有 href 屬性的元素 |
[attribute=value] | $("[href='#']") | 所有 href 屬性的值等於 "#" 的元素 |
[attribute!=value] | $("[href!='#']") | 所有 href 屬性的值不等於 "#" 的元素 |
[attribute$=value] | $("[href $='.jpg']") |
所有 href 屬性的值包含以 ".jpg" 結尾的元素 |
:input | $(":input") | 所有 input 元素 |
:text | $(":text") | 所有 type="text" 的 input 元素 |
:password | $(":password") | 所有 type="password" 的 input 元素 |
:radio | $(":radio") | 所有 type="radio" 的 input 元素 |
:checkbox | $(":checkbox") | 所有 type="checkbox" 的 input 元素 |
:submit | $(":submit") | 所有 type="submit" 的 input 元素 |
:reset | $(":reset") | 所有 type="reset" 的 input 元素 |
:button | $(":button") | 所有 type="button" 的 input 元素 |
:image | $(":image") | 所有 type="image" 的 input 元素 |
:file | $(":file") | 所有 type="file" 的 input 元素 |
:enabled | $(":enabled") | 所有激活的 input 元素 |
:disabled | $(":disabled") | 所有禁用的 input 元素 |
:selected | $(":selected") | 所有被選取的 input 元素 |
:checked | $(":checked") | 所有被選中的 input 元素 |
查找頁面元素的基本方法
.get()
在DOM中查找selector對應的DOM元素,如果匹配多個元素,則返回多個元素.
// 以選擇器定位
cy.get(selector)
// 以別名定位
cy.get(alias)
.find()
在DOM中搜索已經被定位到的元素的后代,並將匹配到的元素返回一個新的jQuery對象(不是元素對象)
//先獲取id為ai的元素,在后代找到class為name元素
cy.get("#ai").find(".name")
.contains()
用來獲取包含指定文本的DOM元素,只會返回匹配的第一個元素
cy.contains(content)
cy.contains(selector, content)
實例代碼
describe('基本查詢元素方法', function () {
beforeEach(function () {
cy.visit("http://192.168.102.49:5001/#/")
});
it('開始測試', function () {
cy.get("#username").type("admin").should("have.value","admin")
cy.get("#root").find("#password").type("123456").should("have.value","123456")
cy.contains("登 錄").click()
});
});
查找頁面元素的輔助方法
輔助定位元素方法 | 描述 |
---|---|
.children() | 用來獲取DOM元素的子元素 |
.parent() | 用來獲取DOM元素的第一層父元素 |
.parents() | 用來獲取DOM元素的所有父元素,包括爺爺級別、祖父級別 |
.siblings() | 用來獲取DOM元素的所有同級元素 |
.first() | 匹配給定的DOM元素列表中的第一個元素,如果是單個DOM元素調用此方法,則返回自己 |
.last() | 匹配給定的DOM元素列表中的最后一個元素,如果是單個DOM元素調用此方法,則返回自己 |
.next() | 獲取給定的DOM元素后面緊跟的下一個同級元素 |
.nextAll() | 獲取給定的DOM元素后面緊跟的所有同級元素 |
.nextUntil(selector) | 獲取給定的DOM元素后面緊跟的所有同級元素,知道遇到Until里定義的元素為止,表示碰到了3,實際上獲取的是2 |
.prev() | 獲取給定的DOM元素前面緊跟的上一個同級元素 |
.prevAll() | 獲取給定的DOM元素前年緊跟的所有同級元素 |
.prevUntil() | 獲取給定的DOM元素前面緊跟的所有同級元素,知道遇到Until里定義的元素位置,表示碰到了3,實際上獲取的是4 |
.each(callbackFn) | 用來遍歷數據或者及其類似的結構(對象有length即可) |
.eq() | 在元素或者數組中的特定所引出獲取DOM元素,作用跟:nth-child()選擇器一樣,只不過下標從0開始 |
.closest() | 獲取匹配的第一個DOM元素(在操作上一條命令返回結果集有詳細的實例) |
.children()
用來獲取DOM元素的子元素
基本語法格式
.children()
.children(selector)
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin','123456')
});
it('.children()', function () {
cy.get('ul').children()
});
it('.child(selector)', function () {
cy.get('ul').children('#li1')
});
});
.parents()、.parent()
帶s的表示獲取DOM元素的所有父元素,不帶s的藐視獲取DOM的第一層父元素
基本語法
.parents()
.parent()
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin','123456')
});
it('.parents()', function () {
cy.get('#li').parents()
});
it('.parent()', function () {
cy.get('#li').parent()
});
});
.siblings()
用來獲取DOM元素的所有同級元素
基本操作語法
.siblings()
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin','123456')
});
it('.siblings()', function () {
cy.get('#li').siblings()
});
});
.first()、.last()
- 匹配給定的DOM元素列表中的第一個元素(如果是單個DOM元素調用此方法,則返回自己)
- 匹配給定的DOM元素列表中的最后一個元素
基本語法
.first()
.last()
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin', '123456')
});
it('.first()', function () {
cy.get('ul>li').first()
});
it('.last()', function () {
cy.get('ul>li').last()
});
});
.next()、.nextAll()、.nextUntil()
- 獲取給定的DOM元素后面緊跟的下一個同級元素
- 獲取給定的DOM元素后面緊跟的所有同級元素
- 獲取給定的DOM元素后面緊跟的所有同級元素,知道遇到Until里定義的元素為止。
基本語法
.next()
.nextAll()
.nextUntil(selector)
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin', '123456')
});
it('.next()', function () {
cy.get('ul>li').next()
});
it('.nextAll()', function () {
cy.get('ul>li').nextAll()
});
it('.nextUntil()', function () {
cy.get('ul>li').nextUntil("#li3")
});
});
.prev()、.prevAll()、.prevUntil()
- 獲取給定的DOM元素前面緊跟的上一個同級元素
- 獲取給定的DOM元素前面緊跟的所有同級元素
- 獲取給定的DOM元素前面緊跟的所有同級元素,知道遇到Until里定義的元素為止
基本語法
.prev()
.prevAll()
.prevUntil(selector)
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin', '123456')
});
it('.prev()', function () {
cy.get('ul>li:nth-child(2)').prev()
});
it('.prevAll()', function () {
cy.get('ul>li:nth-child(2)').prevAll()
});
it('.prevUntil()', function () {
cy.get('ul>li:nth-child(2)').prevUntil("#li3")
});
});
.each()
用來遍歷數據或者及其類似的解耦股(對象有length即可)
基本語法格式
.each(callbackFn)
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin', '123456')
});
it('.prev()', function () {
cy.get('ul>li:nth-child(2)').each(function (el) {
cy.log(el.text())
})
});
});
.eq()
- 在元素或者數組中的特點索引處獲取DOM元素
- 作用跟:nth-child()選擇器一樣,只不過下標從0開始
基本語法
.eq()
操作實例代碼
describe('基本元素輔助方法', function () {
beforeEach(function () {
cy.login('admin', '123456')
});
it('.prev()', function () {
cy.get('ul>li').eq(0)
})
})
.closest()
獲取匹配到的第一個DOM元素(無論是它本身還是它的祖先之一)
基本語法格式
.closest(selector)
.closest(selector, options)
基本用法
// 正確用法
// 找到離 td 標簽元素最近的 .filled 元素
cy.get('td').closest('.filled')
// 錯誤用法
// 不能通過 cy 直接調用
cy.closest('.active')
// url() 返回的並不是 DOM 元素
cy.url().closest()
option基本參數
- log 是否將命令顯示到命令日志中,默認true
- timeout:命令超時時間
實例代碼
describe('登錄', function () {
beforeEach(function () {
cy.visit("http://192.168.102.49:5001/#/")
});
it("測試closest", function () {
cy.get("#username").type("admin")
cy.get("#password").type("123456")
cy.get("button[type=submit]").click()
cy.get("ul[role=menu]").closest("li").click()
});
});
cypress操作頁面元素
命令 | 作用 |
---|---|
.type() | 輸入框輸入文本元素 |
.focus() | 聚焦DOM元素 |
.blur() | DOM元素失去焦點 |
.clear() | 清空DOM元素 |
.submit() | 提交表單 |
.click() | 點擊DOM元素 |
.dbclick() | 雙擊 |
.rightclick() | 右鍵點擊 |
.check() | 選中單選框、復選框 |
.uncheck() | 取消選中復選框 |
.select() | select options選項框 |
.scrollIntoView() | 將DOM元素滑動到可視區域 |
.trigger() | DOM元素上觸發事件 |
cy.scrollTo() | 滑動滾動條 |
.type()
基本語法格式
// 輸入文本
.type(text)
// 帶參數輸入文本
.type(text, options)
正常文本實例
it('正常實例', function () {
cy.get('input').type("正常實例")
});
特殊字符實例
it('輸入特殊字符', function () {
cy.get('input').type("{del}")
});
所支持特殊字符表
特殊字符 | 描述 |
---|---|
{{} | 輸入{ |
{backspace} | 鍵盤的backspace鍵 |
{del} | 鍵盤的del鍵 |
{downarrow} | 鍵盤的↓鍵 |
{leftarrow} | 鍵盤的←鍵 |
{rightarrow} | 鍵盤的→鍵 |
{uparrow} | 鍵盤的↑鍵 |
{end} | 鍵盤的end鍵,將光標移動到行尾 |
{enter} | 回車鍵 |
{esc} | 退出鍵 |
{home} | 鍵盤的home鍵,將光標移動到行首 |
{insert} | 的鍵盤insert鍵 |
{pagedown} | 鍵盤的pagedown,向下滾動 |
{pageup} | 鍵盤pageup,向上滾動 |
{selectall} | 通過創建選擇范圍來選擇所有文本 |
.focus()、.blur()
聚焦與失焦DOM元素
- 必須是DOM元素才能調用.focus()、.blur()方法,不一定是輸入框
- 確保DOM元素是可聚焦、失焦的
基本語法格式
.focus()
.focus(options)
.blur()
.blur(options)
基本實例
//input進行聚焦
cy.get('input').first().focus()
// 輸入內容后,再讓輸入框失焦
cy.get('[type="email"]').type('me@email.com').blur()
// 先聚焦再失焦
cy.get('[tabindex="1"]').focus().blur()
所支持的參數傳遞
Options | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 執行.type()之前的等待超時時間 |
force | false | 強制執行操作,禁用等待執行操作 |
.clear()
- 表示清空輸入框的所有內容
- .clear()等價於.type("{selectall}{backspace}")
基本語法格式
.clear()
.clear(options)
基本實例
cy.get('.action-clear').type('Clear this text')
.should('have.value', 'Clear this text')
.clear()
.should('have.value', '')
// 下面.clear()等價於.type('{selectAll}{backspace}')
it('clear()', function () {
cy.get('input').type('qq').clear()
});
it('.type({},{})', function () {
cy.get('input').type('{selectAll}{backspace}')
});
所支持的參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
force | false | 強制還行操作,禁用等待執行操作 |
timeout | defaultCommandTimeout | 等待執行.clear()之前的超時時間 |
.submit()
必須是form元素才能調用.submit()
基本語法格式
.submit()
.submit(options)
基本實例
it('form操作', function () {
cy.get('form').submit()
});
所支持的參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.click()、.dblclick()、.rightclick()
三個點擊事件的不同用法大同小異
基本語法格式
// 單擊某個元素
.click()
// 帶參數的單擊
.click(options)
// 在某個位置點擊
.click(position)
// 在某個位置點擊,且帶參數
.click(position, options)
// 根據頁面坐標點擊
.click(x, y)
// 根據頁面坐標點擊,且帶參數
.click(x, y, options)
基本實例
// 單擊DOM元素(常規用法)
cy.get('#action-canvas').click('bottom')
// 雙擊DOM元素
cy.get('.action-div').dblclick().should('not.be.visible')
// 右擊DOM元素
cy.get('.action-div').rightclick().should('not.be.visible')
所支持的位置參數傳遞
總共有9個參數值
it('.click(position)', function () {
cy.get('#main1').click('top')
cy.get('#main1').click('topLeft')
cy.get('#main1').click('topRight')
cy.get('#main1').click('left')
cy.get('#main1').click('right')
cy.get('#main1').click('bottom')
cy.get('#main1').click('bottomLeft')
cy.get('#main1').click('bottomRight')
cy.get('#main1').click('center')
});
所支持的橫縱坐標參數
it('.click(x,y)', function () {
cy.get('#main1').click(15,17)
});
所支持的參數傳遞
- cypress可以通過Test Runner的快照找到阻止DOM元素交互的情況,但某些情況下可能會阻礙測試的進行
- 場景:有一個嵌套的導航結構,用戶必須將鼠標hover在一個非常特定的模式中,才能拿到所需的鏈接,當測試的時候,我們只是想獲取鏈接而已,過多的繁瑣操作會導致測試失敗.
- 當設置force:true時,cypress會強制操作命令的發生,避開前面的所有檢查,可以傳遞{force:true}給大多數操作命令
option | 默認值 | 描述 |
---|---|---|
log | true | 在命令日志中顯示命令 |
force | false | 強制執行操作,禁用等待執行操作 |
multiple | false | 連續點擊多個元素 |
timeout | defaultCommandTimeout | 執行.click()之前的等待超時時間 |
混合實例
it('.click(options)', function () {
cy.get('#btn').click({force:true})
});
it('.click(position,options)', function () {
cy.get('#btm').click('top',{force:true})
});
it('.click(x,y,options)', function () {
cy.get('#btn').click(15,17,{force:true})
});
it('.click({multiple:true})', function () {
cy.get('btn').click({multiple:true})
});
.check()、.uncheck()
- 檢查選項框是否被選中,針對input標簽的單選框或復選框達到選中的作用
- 和check()作用相反,取消選中復選框,注意的是,只有復選框checkbox可以使用uncheck(),其他的語法格式、寫法和check()一樣.
基本語法格式
// 所有匹配到的選擇框都會被選中一遍
.check()
// 選中指定值的選項
.check(value)
// 選中多個選項(多選框)
.check(values)
// 所有匹配到的選擇框都會被選中一遍,且帶參數
.check(options)
// 選中指定值的選項,且帶參數
.check(value, options)
// 選中多個選項(多選框),且帶參數
.check(values, options)
基本實例
it('case1', function () {
// 表示選中所有的復選框
cy.get('[type="checkbox"]').check()
// 表示只選中第一個復選框
cy.get('[type="checkbox"]').first().check()
});
附:只有type類型為checkbox或者radio才可以調用.check()
.check(value)
it('case1', function () {
// 表示選中value值為sz的復選框
cy.get('[type="checkbox"]').check("sz")
// 表示選中value值為monkey的復選框
cy.get('[type="checkbox"]').check("money")
});
.check(values)
it('case1', function () {
// 表示選中value值為sz和money的復選框
cy.get('[type="checkbox"]').check(["sz","money"])
});
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.select()
基本語法格式
// 選中指定值的選項
.select(value)
// 選中指定值的多個選項
.select(values)
// 選中指定值的選項,且帶參數
.select(value, options)
// 選中指定值的多個選項,且帶參數
.select(values, options)
基本實例
// 選擇值為user的option(注意的是get里面的select必須是元素)
cy.get('select').select('user')
.select(value)
it('.select(value)', function () {
cy.get('select').eq(0).select('1').should('contain.text','apples')
cy.get('select').eq(0).select('orange').should('contain.value','2')
});
.select(values)
//選中多選框中的多條數據
it('.select(values)', function () {
cy.get('select').eq(0).select(['1', '2']).invoke('val').should('deep.equal', ['1', '2'])
});
.select(value,options)
//因為默認的第二個元素是隱藏的所以不加force:true會報錯,並且在報錯的時候會提示加上force:true
it('.select(value,options)', function () {
cy.get('select').eq(1).select('1',{force:true}).should('contain.value', 'apples')
});
注意的是,如果在選中元素的情況下,加了disabled,表示不可選擇,那么即使加了force:true依然會報錯
.trigger()
在DOM元素上觸發指定事件
基本語法格式
// eventName表示觸發的事件名稱,position表示位置(topLeft、top、topRight、left、center、right、bottomLeft、bottom、bottomRight)
.trigger(eventName)
.trigger(eventName, position)
.trigger(eventName, options)
.trigger(eventName, x, y)
.trigger(eventName, position, options)
.trigger(eventName, x, y, options)
基本實例
//區別mousedown與cleck,cleck是按下抬起觸發,mousedown是按下觸發
it('mousedown事件', function () {
cy.get('a').trigger('mousedown')
});
//鼠標懸停,hover效果
it('鼠標懸停,hover', function () {
cy.get('a').trigger('mouseover')
});
it('左鍵長按操作,三秒后抬起', function () {
cy.get('a').trigger('mousedown', {force: true, button: 0}).wait(3000).trigger("mouseleave", {force: true})
});
it('長按操作,鼠標的左鍵、滾輪、右鍵點擊', function () {
cy.get('a').trigger('mousedown', {force: true, button: 0})
cy.get('a').trigger('mousedown', {force: true, button: 1})
cy.get('a').trigger('mousedown', {force: true, button: 2})
});
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
force | false | 強制執行操作,禁用等待執行操作 |
timeout | defaultCommandTimeout | 等待執行.trigger()之前的超時時間 |
cancelable | true | 事件是否取消 |
bubbles | true | 事件是否可冒泡傳遞 |
以上是通用的option,以下還有一些事件特有的options | ||
clientX、clientY | 相當於瀏覽器左上角的距離 | |
pageX、pageY | 相當於整個頁面左上角的距離 | |
screenX、screenY | 相當於電腦屏幕左上角的距離 |
指定事件不應該冒泡
// 默認情況下,指定的事件將在DOM樹中冒泡,false可以防止事件冒泡
cy.get('#s-usersetting-top').trigger('mouseover', {bubbles: false})
設置clientX和clientY
覆蓋基於元素本身的默認自動定位(x,y),對於mousemove之類的事件很有用,可能需要將元素拖動到元素本身之外的地方,基於瀏覽器本身的X、Y坐標
cy.get('button').trigger('mousemove', { clientX: 200, clientY: 300 })
.scrollIntoView()
將元素滾動到視圖中,需要注意的是,Cypress運行的命令快照不會顯示滾動的過程,如果要查看滾動的過程,需要用.pause()遍歷每個命令,或者通過觀察測試運行的視頻.
基本語法
.scrollIntoView()
.scrollIntoView(options)
基本實例
// 將 footer 元素 滾動到視圖中
cy.get('footer').scrollIntoView()
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
duration | 0 | 滾動持續的時間,ms為單位 |
easing | swing | 滾動的動畫效果 |
offset | {top:0,left:0} | 元素滾動后的偏移量 |
.scrollTo()
瀏覽器自帶的滾動條
基本語法格式
// 可以是cy直接調用,也可以是DOM元素來調用position表示位置(topLeft、top、topRight、left、center、right、bottomLeft、bottom、bottomRight)
cy.scrollTo(position)
cy.scrollTo(x, y)
cy.scrollTo(position, options)
cy.scrollTo(x, y, options)
// ---或---
.scrollTo(position)
.scrollTo(x, y)
.scrollTo(position, options)
.scrollTo(x, y, options)
基本實例
必須是DOM元素才能調用,而且可以是針對瀏覽器窗口,也可以針對有滾動條的元素.
// 整個頁面往下滑動 500px
cy.scrollTo(0, 500)
// 滾動 .sidebar 元素到它的底部
cy.get('.sidebar').scrollTo('bottom')
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
duration | 0 | 滾動持續的時間,ms為單位 |
easing | swing | 滾動的動畫效果 |
獲取頁面全局對象
命令 | 作用 |
---|---|
.window() | 獲取當前頁面的窗口對象 |
.title() | 獲取當前頁面的title |
.url() | 獲取當前頁面的URL |
.location() | 獲取當前頁面的全局window.location對象 |
.document() | 獲取當前頁面的全局window.document對象 |
.hash() | 獲取當前頁面的URL 哈希值 |
.root() | 獲取根DOM元素 |
.window()
獲取當前頁面的window對象
基本語法格式
cy.window()
cy.window(options)
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.title()
獲取頁面的標題
基本語法格式
cy.title()
cy.title(options)
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.url()
獲取當前頁面的url,等價於cy.location('href')
基本語法格式
cy.url()
cy.url(options)
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.location()
獲取當前頁面的window.location
基本語法格式
cy.location()
cy.location(key)
cy.location(options)
cy.location(key, options)
基本實例
cy.location()
// 獲取 host 值
cy.location('host')
// 獲取 port 值
cy.location('port')
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
location對象的其他屬性
屬性 | 解釋 |
---|---|
hash | 獲取URL的錨部分 |
host | 獲取當前的主機名稱和端口 |
hostname | 獲取當前的主機名 |
href | 獲取當前的鏈接 |
origin | 獲取當前協議 |
pathname | 獲取當前的路徑名 |
port | 獲取url的端口 |
protocol | 返回當前協議 |
search | 獲取當前URL的搜索部分 |
toString | --- |
.document()
獲取當前頁面的window.document
基本語法格式
cy.document()
cy.document(options)
基本操作實例
describe('測試百度', function () {
context('搜索功能', function () {
beforeEach(function () {
cy.visit("")
});
it('測試.document()', function () {
cy.document()
});
})
});
.hash()
獲取頁面當前的url的哈希值,等價於cy.location('hash')
基本語法
cy.hash()
cy.hash(options)
可支持的參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.root()
獲取當前根元素
基本語法格式
cy.root()
cy.root(options)
基本操作實例
describe('測試百度', function () {
context('搜索功能', function () {
beforeEach(function () {
cy.visit("")
});
it('測試.root()', function () {
cy.root()
cy.get("#kw").within(function(){
cy.root()
})
});
})
});
操作瀏覽器
命令 | 作用 |
---|---|
.go() | 瀏覽器前進、后退 |
.reload() | 刷新頁面 |
.viewport() | 控制瀏覽器窗口的大小和方向 |
.visit() | 訪問指定的 url |
.wait() | 強制等待 |
.go()
在瀏覽器歷史記錄中,訪問前一個或后一個URL
基本語法格式
cy.go(direction)
cy.go(direction, options)
基本實例
// 相當於單擊瀏覽器左上角的后退←按鈕
cy.go("back")
// 相當於單擊瀏覽器左上角的前進→按鈕
cy.go("forward")
//上面兩條等價於下面兩條
// 相當於單擊瀏覽器左上角的后退←按鈕
cy.go(-1)
// 相當於單擊瀏覽器左上角的前進→按鈕
cy.go(1)
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.reload()
刷新頁面
基本語法格式
cy.reload()
cy.reload(forceReload)
cy.reload(options)
cy.reload(forceReload, options)
forceReload
- 是否在不使用緩存的情況下重新加載當前頁面
- true表示強制重新加載不適用緩存,所有資源文件都會重新拉取一遍,好處就是可從取服務器獲取最新的資源文件,壞處就是加載時間會變長
基本實例
cy.reload()
cy.reload(true)
可支持的帶參數傳遞
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
timeout | defaultCommandTimeout | 等待執行.submit()之前的超時時間 |
.viewport()
控制瀏覽器窗口的尺寸和方向,也可以通過在配置項中定義viewportWidth和viewportHeight來全局設置瀏覽器窗口的寬度和高度.,默認高度1000px*660px
基本語法格式
cy.viewport(width, height)
cy.viewport(preset, orientation)
cy.viewport(width, height, options)
cy.viewport(preset, orientation, options)
參數width、height
- 必須為非負數
- 像素單位px
參數options
Option | Default | Description |
---|---|---|
log | true | 在命令日志中顯示命令 |
參數orientation
- 默認是縱向,portrait
- 可改橫向,landscape
參數preset
預設值,Cypress提供了一下的預設值
預設值 | 寬度 | 高度 |
---|---|---|
ipad-2 | 768 | 1024 |
ipad-mini | 768 | 1024 |
iphone-3 | 320 | 480 |
iphone-4 | 320 | 480 |
iphone-5 | 320 | 568 |
iphone-6 | 375 | 667 |
iphone-6+ | 414 | 736 |
iphone-x | 375 | 812 |
iphone-xr | 414 | 896 |
macbook-11 | 1366 | 768 |
macbook-13 | 1280 | 800 |
macbook-15 | 1440 | 900 |
samsung-note9 | 414 | 846 |
samsung-s10 | 360 | 760 |
基本實例
需要注意的是,cy.viewport()后面不能在鏈接其他命令
//550px*750px
cy.viewport(550,750)
//設置iphone6的尺寸
cy.viewport("iphone-6")
自動縮放
- 默認情況下,如果屏幕不夠大,無法顯示應用程序所有像素,則Cypress會將應用程序縮放並居中,以適應Cypress的Test Runner
- 縮放應用程序不會影響應用程序的任何計算或行為
- 自動縮放的好處:無論屏幕大小如何,測試都始終通過或失敗,測試最終在CI中運行,因此無論Cypress在什么計算機上運行,所有viewports都將相同.
Cypress.config()
可以通過設置全局的方式,設定viewport的寬高
Cypress.config('viewportWidth',800)
//默認800
Cypress.config('viewportWidth')
.visit()
訪問遠程URL
基本語法格式
cy.visit(url)
cy.visit(url, options)
cy.visit(options)
URL
- 兩種值,一種是需要直接訪問的URL,可以是一個完整的URL,比如:https://www.cnblogs.com
- 第二種是html文件的相對路徑,路徑是相對於Cypress的安裝目錄,不需要file://前綴
可執行的帶參數傳遞
參數(options) | 默認 | 作用 |
---|---|---|
method | GET | 請求方法,GET或POST |
body | null | l 與POST請求一起發送的數據體l 如果是字符串,則將其原封不動地傳遞l 如果是一個對象,它將被URL編碼為字符串,並加上Content-Type:application / x-www-urlencoded |
headers | {} | 請求頭 |
qs | null | Url的請求參數 |
log | true | 是否打印日志 |
auth | null | 添加基本授權標頭 |
failOnStatusCode | true | 是否在2xx和3xx以外的響應代碼上標識為失敗 |
onBeforeLoad | function | 在頁面加載所有資源之前調用指定的方法 |
onLoad | function | 頁面觸發加載事件后調用 |
retryOnStatusCodeFailure | false | 當狀態碼是錯誤碼時,Cypress是否自動重試,最多重試4次 |
retryOnNetworkFailure | true | 當網絡錯誤時,Cypress是否自動重試,最多重試4次 |
timeout | pageLoadTimeout | 最長等待 .visit() 完成的時間 |
基本實例
// 在新的窗口打開 URL
cy.visit('http://localhost:3000')
cy.visit('./pages/hello.html')
// 添加timeout
cy.visit('http://localhost:3000',{timeout:3000})
baseUrl
- 建議在使用cy.visit()時,在cypress.json里設置一個baseUrl
- baseUrl相當於一個全局共享的host,在使用visit()和request()等命令時自動將baseUrl傳遞進去
- 優勢:首次啟動Cypress測試時,添加baseUrl還可以節省一些時間
- 不添加baseUrl的影響就是一旦遇到cy.visit(),Cypress便將主窗口的URL切換到訪問指定的URL,首次開始測試時,可能會導致刷新或重新加載.
baseUrl實例
{
"baseUrl": "http://192.168.102.50:6434/",
"env": {
"foor": "#root main>div>div>div:nth-child(1)>div>div>div>div>div:nth-child(1)>div:nth-child(2)",
"key": "value"
}
}
.wait()
等待數毫秒或等待別名資源解析,然后在繼續執行下一個命令.
基本語法格式
cy.wait(time)
cy.wait(alias)
cy.wait(aliases)
cy.wait(time, options)
cy.wait(alias, options)
cy.wait(aliases, options)
參數time
等待的時間(一毫秒為單位)
參數alias、aliases
- 使用.alias()命令定義兵役@字符和別名命名的別名路由
- alias組成的數組
參數alias的基本實例
it.skip('基本實例', function () {
cy.server()
cy.route({
url: '**/login',
status: 503,
response: {
success: false,
data: 'Not success'
},
}).as("login")
cy.get("input[name=username]").type(username)
cy.get("input[name=password]").type(`${password}{enter}`)
// 等待請求的響應
cy.wait('@login').then((res) => {
// 針對響應進行斷言
expect(res.status).to.eq(503)
expect(res.responseBody.data).to.eq('Not success')
expect(res.responseHeaders).to.have.property('x-token', 'abc-123-foo-bar')
})
});
參數aliases的基本實例
it.skip('基本實例', function () {
cy.server()
cy.route('users/*').as('getUsers')
cy.route('activities/*').as('getActivities')
cy.route('comments/*').as('getComments')
cy.visit('/dashboard')
cy.wait(['@getUsers', '@getActivities', '@getComments']).then((xhrs) => {
// xhrs現在將是匹配的XHR的數組
// xhrs[0] <-- getUsers 的響應
// xhrs[1] <-- getActivities 的響應
// xhrs[2] <-- getComments 的響應
})
});
參數options
參數 | 默認 | 作用 |
---|---|---|
log | True | 是否打印日志 |
timeout | requestTimeout,responseTimeout | 等待.wait()完成的時間 |
requestTimeout | 5000 | 請求超時時間 |
responseTimeout | 30000 | 響應超時時間 |
基本實例
cy.wait(500)
cy.wait('@getProfile')
測試報告
內置的測試報告包括Mocha的內置報告和直接嵌入在Cypress中的測試報告,主要有三種:spec格式報告、json格式、junit格式
前置工作
- 確保package.json文件的scripts模塊加入了如下鍵值對"cypress:run":"cypress run"
- cypress run是以無頭瀏覽器模式跑測試用例,用例文件夾下的所有測試用例
- cypress open會打開測試用例集的解密那,需要手動運行
spec格式報告
spec格式是Mocha的內置報告,它的輸出是一個嵌套的分級視圖
進入Cypress安裝的目錄,執行命令 npm run cypress:run --reorter=spec
json格式報告
json測試報告格式將輸出一個大的json對象
在Cypress中使用json格式的報告非常簡單,在命令行運行時加上--reporter=json
進入Cypress安裝的目錄,執行命令 npm run cypress:run --reporter=json --reporter-options "toConsole=true"
junit格式報告
junit測試報告格式將輸出一個xml文件
在Cypress中使用xml格式的報告非常簡單,在命令行運行時加上 --reporter junit
進入Cypress安裝的目錄,執行命令 npm run cypress:run --reporter junit --reporter-options "mochaFile=results/test_output.xml,toConsole=true"
Mochawesome測試報告
除了內置的測試報告,Cypress也支持用戶自定義報告格式.
第一步
將Mocha、Mochawesome添加至項目中
npm install --save-dev mocha
npm install --save-dev mochawesome
注意點
- 先查看node_modules目錄下是否有mocha文件夾,如果有直接裝mochawesome
- 如果安裝mocha失敗,出現很古怪的錯誤,比如mkdirp版本不行
- 嘗試先update mkdirp庫,如果也報錯,則uninstall mkdirp庫,如果仍然報錯,則把Cypress目錄下的node_moudules整個文件夾刪掉,重新自行npm install,大概率就可以解決問題了
第二步
進入Cypress安裝目錄,執行命令 npm run cypress:run --reporter mochawesome
用戶自定義報告的步驟
第一步:配置reporter選項
- cyress.json文件中配置reporter選項,指定reporter文件位置。
- 把reporter定義在custom_reporter.js文件中
編寫自定義報告文件
- 進入 Cypress 安裝目錄下的cypress目錄下
- 創建 reporter 文件夾,然后創建一個custom_reporter.js文件
- 寫以下代碼(此自定義報告擴展了內置報告,僅更改了成功的顯示樣式)
var mocha = require('mocha');
module.exports = MyReporter;
function MyReporter(runner) {
mocha.reporters.Base.call(this, runner)
var passes = 0
var failures = 0
runner.on('pass', function (test) {
passes++
console.log('pass:%s', test.fullTitle())
})
runner.on('fail', function (test, err) {
failures++
console.log('fail:%s -- error:%s', test.fullTitle(), err.message)
})
runner.on('end', function () {
console.log('用戶自定義報告:%d/%d', passes, passes + failures)
})
}
第三步:運行測試
輸入命令:yarn cypress:run --reporter ../cypress/reporters/custom_reporter.js
命令行運行Cypress
- 在交互模式下打開Cypress測試運行器
- 在測試用例的運行過程中,測試用例的每一條命令,每一個操作都將顯式的顯示在測試運行器中
前置操作
通過package.json指定scripts,在package.json文件下,添加"cypress:open":"cypress open"
yarn運行
yarn cypress:open
npm運行
npm run cypress:open
cypress open詳解
- cypress open運行時支持指定多個參數,指定的參數將自動應用於你通過測試運行器打開的項目
- 這些參數講應用於每一次測試運行,直到關閉測試運行器為止
- 指定的參數將會覆蓋配置文件cypress.json中的相同參數
可選參數列表明細
可選參數 | 含義 | 實例命令 |
---|---|---|
--browser,-b | 指定運行測試的瀏覽器 | cypress open --browser /usr/bin/chromium |
--config,-c | 指定運行時的配置項 | cypress open --config pageLoadTimeout=100000,watchForFileChanges=false |
--config-file,-C | 指定運行時的配置文件,默認情況配置項都定義在cypress.json中 | cypress open --config-file tests/cypress-config.json |
--detached,-d | 以靜默模式打開Cypress | |
--env,-e | 定義環境變量 | 單個環境變量 cypress open --env host=api.dev.local 多個環境變量 cypress open --env host=api.dev.local,port=4222值為 json 字符串 cypress open --env flags='{"feature-a":true,"feature-b":false}' |
--global | 全局模式,允許多個嵌套項目中共享同一個安裝好的Cypress版本 | cypress open --global |
--help,-h | 幫助文檔 | |
--port,-p | 重載默認端口 | cypress open --port 8080 |
--project,-P | 指定項目路徑,用來指定待運行的項目,如果你的項目包含多個子項目,可以用此參數運行指定的子項目 | cypress open --project ./some/nested/folder |
cypress-skip-and-only-ui插件
前言
- 一個測試用例集通常包含多個測試用例
- 當網絡不穩定而引起測試失敗時,我們希望進重試失敗的用例而不是重跑整個測試用例集
- 單測試運行器默認進支持重試整個測試用例集
安裝
npm i -D cypress-skip-and-only-ui
配置
在cypress/supprt/index.js文件,任意位置添加配置項如下:
require('cypress-skip-and-only-ui/support')
在cypress/plugins/index.js文件,任意位置添加配置如下:
const task=require('cypress-skip-and-only-ui/task')
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('task',task)
}
現在新版本的Cypress是不支持這個插件的,但是不影響真實使用,畢竟這也是調試作用居多.
操作上一條命令返回結果的命令集
命令 | 作用 |
---|---|
then() | 將上一條命令返回的結果注入到下一個命令中 |
and() | 創建一個斷言。斷言將自動重試,直到它們通過或超時 |
should() | and() 的別名 |
invoke() | 對上一條命令的結果執行調用方法操作 |
its() | 獲取屬性值 |
as() | 取別名 |
within() | 限定命令作用域 |
each() | 遍歷當前元素 |
spread() | 將數組內容作為單獨的參數傳回到回調函數 |
closest() | 上一條命令返回的DOM元素,在獲取匹配的第一個DOM元素 |
.then()
作用
- 在Cypress中,保存一個值或者引用的最好方式是使用閉包
- then()就是Cypress對閉包的一個典型應用
- the()返回的是上一個命令的結果,並將其注入到下一個命令中
基本語法格式
// options:只有timeout,4000s
// callbackFn:回調函數,需要將上一個命令的結果作為參數進行傳遞
.then(callbackFn)
.then(options, callbackFn)
基本實例
it('then()命令', function () {
cy.get("#ui").then(function (t) {
cy.log(t.text())
})
});
通過then和should比較文本是否相等
it('then()比較值', function () {
cy.get("#ui").then(function (t) {
const txt=t.text()
cy.get("#li1").should(function (t2) {
expect(t2.text()).to.be.eq(txt)
})
})
});
.and()、.should()
作用
- 創建一個斷言,斷言將自動重試,直到它們通過或超時
- 和should()一個用法
基本語法格式
// chainers:斷言器
// value:需要斷言的值
// method:需要調用到的方法
// callbackFn:回調方法,可以滿足自己想要斷言的內容,且總是返回前一個cy命令返回的結果,方法內的return是無效的,會一直運行直到里面沒有斷言
.and(chainers)
.and(chainers, value)
.and(chainers, method, value)
.and(callbackFn)
.and()返回的結果
//在大多數情況下,.and()返回與上一個命令相同的結果
cy
.get('nav') // 返回 <nav>
.should('be.visible') // 返回 <nav>
.and('have.class', 'open') // 返回 <nav>
//但是,某些chainer會改變返回的結果
cy
.get('nav') // 返回 <nav>
.should('be.visible') // 返回 <nav>
.and('have.css', 'font-family') // 返回 'sans-serif'
.and('match', /serif/) // 返回 'sans-serif'
對同一結果的操作實例
cy.get('button').should('have.class', 'active').and('not.be.disabled')
chainer改變返回結果的實例
<!--html代碼-->
<li>
<a href="users/123/edit">Edit User</a>
</li>
// cypress代碼
cy
.get('a')
.should('contain', 'Edit User') // 返回的是 <a>
.and('have.attr', 'href') // 返回的是 href 的值
.and('match', /users/) // 返回的是 href 的值
.and('not.include', '#') // 返回的是 href 的值
method+value的實例
斷言href屬性值是否等於/users
cy
.get('a')
.should('have.class', 'active')
.and('have.attr', 'href', '/users')
.invoke()
作用
對前一條命令返回的結果進行調用方法
基本語法格式
// functionName:需要調用的方法名
// options:log和timeout
// args:傳遞給函數的參數,數量沒有限制
.invoke(functionName)
.invoke(options, functionName)
.invoke(functionName, args...)
.invoke(options, functionName, args...)
基本實例
// 調用 animate 方法
cy.wrap({ animate: fn }).invoke('animate')
// 找到.modal 元素並調用 show 方法
cy.get('.modal').invoke('show')
混合實例
// 斷言函數返回值
it('斷言函數返回值', function () {
const fn=function () {
return "bar"
}
cy.wrap({foo:fn}).invoke('foo').and('eq','bar')
});
// 調用函數並傳遞參數
it('調用函數並傳遞參數', function () {
const fn=function (a,b,c) {
return a+b+c
}
cy.wrap({sum:fn}).invoke('sum',1,2,3).should("be.eq",6).and("be.lt",10)
});
// 作為函數的屬性被調用
it('作為函數的屬性被調用', function () {
cy.get('#content').should('be.hidden').invoke('show').should('be.visible').find('input[name="email]').type('text')
});
.its()
作用
獲取上一條命令結果的屬性值
基本語法格式
// propertyName:索引、屬性名、要獲取的嵌套屬性名稱
// options:log與timeout
.its(propertyName)
.its(propertyName, options)
基本實例
cy.wrap({ width: '50' }).its('width') // 獲取寬度屬性
cy.window().its('sessionStorage') // 獲取 sessionStorage 屬性
混合實例
// 獲取字典對象的屬性值
cy.wrap({age: 52}).its('age').should('eq', 52) // true
// 數組對象,根據索引取值
cy.wrap(['polo', 'yy']).its(1).should('eq', 'yy')
// 獲取元素的屬性值
cy.get('ul li').its('length').should('be.gt',4)
// 獲取字符串對象的屬性值
cy.url().its('length').should('be.gt', 20)
// 屬性值是函數
const fn = () => {
return 42
}
cy.wrap({getNum: fn}).its('getNum').should('be.a', 'function')
//獲取嵌套屬性值
const user = {
contacts: {
work: {
name: 'Kamil'
}
}
}
cy.wrap(user).its('contacts.work.name').should('eq', 'Kamil') // true
.as()
作用
- 起別名以供以后使用
- 可在cy.get()或cy.wait()命令中引用別名
基本語法格式
.as(aliasName)
基本實例
// 給第一個 li 元素起別名
cy.get('.main-nav').find('li').first().as('firstNav')
// 給網絡請求的響應起別名
cy.route('PUT', 'users', 'fx:user').as('putUser')
引用別名的方式
cy.get()或cy.wait()命令中使用@前綴引用過的別名名稱,如@firstNav、@putUser
基本調用實例
一般使用.wrap()和.as()混合使用
describe('測試百度', function () {
context('搜索功能', function () {
beforeEach(function () {
cy.visit('https://www.baidu.com')
cy.contains("百度首頁").then(function (name) {
cy.wrap(name.text()).as('input')
});
});
it('使用as', function () {
cy.get("#kw").type("qq")
cy.get("#su").click()
})
it('調用as ', function () {
cy.get('@input').then(function (name2) {
cy.log(name2)
})
});
})
});
within()
將所有后續cy命令的作用域限定在此元素中,在特定的元素組中工作時很有用.
基本語法格式
.within(callbackFn)
.within(options, callbackFn)
參數解釋
- callbackFn 回調函數,第一個參數是上一條命令的返回結果(必須是元素)
- log:是否將命令顯示到命令日志中,默認true
基本實例
cy.get('form').within(($form) => {
// 在回調函數里,cy 命令的作用域將限定在 form 中(不能直接通過cy調用).並且返回的是跟cy.get('form')命令一樣的結果,在回調函數里面的操作都是在form中進行的
})
.each()
遍歷數組數據結構(具有length屬性的數組或對象)
基本語法格式
.each(callbackFn)
// callbackFn 回調函數,可以擁有三個參數:value、index、collection
基本實例
返回的結果和上一條命令一樣
// 遍歷每個 li 元素
cy.get('ul>li').each(() => {...})
// 遍歷每個 cookie
cy.getCookies().each(() => {...})
// 遍歷每個li元素,並且打印出來
it('.each()', function () {
cy.get('li').each(function (item,index,name) {
cy.log(item,index,name)
})
});
.spread()
將數組拆分為多個參數
基本語法格式
callbackFn:回調函數,將數組拆分后作為函數的多個參數
.spread(callbackFn)
.spread(options, callbackFn)
操作實例
spread命令不會出現在命令日志中,spread命令的回調函數的參數個數無論是多了還是少了都不會報錯,少了不會獲取后面的值,多了則是一個空格.
describe('測試spread', function () {
context('帶li標簽', function () {
beforeEach(function () {
cy.visit('https://example.cypress.io/commands/connectors')
});
it('測試li', function () {
const arr=['foo','bar','baz']
cy.wrap(arr).spread(function (foo,bar,baz) {
expect(foo).to.equal('foo')
expect(bar).to.eq('bar')
expect(baz).to.eq('baz')
})
});
});
});
操作文件相關命令
.fixture()
加載位於文件中的一組固定數據
基本語法格式
cy.fixture(filePath)
cy.fixture(filePath, encoding)
cy.fixture(filePath, options)
cy.fixture(filePath, encoding, options)
參數解釋
- filepath,文件路徑默認會從cypress/fixtures 文件夾下找文件
- encoding
- ascii
- base64
- binary
- hex
- latin1
- utf8
- utf-8
- ucs2
- ucs-2
- utf16le
- utf-16le
基本實例
// 從 users.json 文件中加載數據
cy.fixture('users').as('usersJson')
cy.fixture('logo.png').then((logo) => {
// 加載 logo.png
})
注意使用方式
一般情況下,我們使用命令cy.fixture('admin').as('adminJSON'),admin.json是不指定后綴,直接使用admin,但是cypress有自己的讀取規則,讀取順序如下:
- cypress/fixtures/admin.json
- cypress/fixtures/admin.js
- cypress/fixtures/admin.coffee
- cypress/fixtures/admin.html
- cypress/fixtures/admin.txt
- cypress/fixtures/admin.csv、
- cypress/fixtures/admin.png
- cypress/fixtures/admin.jpg
- cypress/fixtures/admin.jpeg
- cypress/fixtures/admin.gif
- cypress/fixtures/admin.tif
- cypress/fixtures/admin.tiff
- cypress/fixtures/admin.zip
讀取數據的實例
第一步:cypress/fixture/下創建user.json文件,並寫入如下內容
[
{
"name": "#kw"
}
]
第二步:編寫測試用例
describe('測試百度', function () {
context('測試百度搜索', function () {
beforeEach(function () {
cy.visit('https://www.baidu.com')
});
it('測試qq', function () {
cy.fixture('user').then(function(user){
// 怎么獲取json,就怎么寫
cy.get(user[0].name).type('qq')
cy.get("#su").click()
})
});
});
});
.readFile()
將讀取文件並返回內容
基本語法格式
cy.readFile(filePath)
cy.readFile(filePath,encoding)
cy.readFile(filePath,options)
cy.readFile(filePath,encoding,options)
相關參數
- filePath 項目根目錄(包含默認cypress.json配置文件的目錄)中需要讀取的文件路徑
- encoding 讀取時需要使用的編碼
- ascii
- base64
- binary
- hex
- latin1
- utf8
- utf-8
- ucs2
- ucs-2
- utf16le
- utf-16le
- options
- log 是否將命令顯示到命令日志中,默認true
- timeout 命令超時時間
操作實例代碼
describe('測試讀取', function () {
it('讀取text文件', function () {
cy.readFile('sss.text').then(function (el) {
cy.log(el)
})
});
it('讀取json文件', function () {
cy.readFile('sss.json').then(function (el) {
cy.log(el.name)
})
});
/*
* yaml擴展使用
* YAML = require('yamljs');
*
* // 解析 YAML 文件
* nativeObject = YAML.parse(yamlString);
*
* // 生成 YAML 字符串
* yamlString = YAML.stringify(nativeObject, 4);
*
* // 加載 YAML 文件
* nativeObject = YAML.load('myfile.yml');
*/
it('讀取yaml文件', function () {
cy.readFile('sss.yaml').then(function (yaml) {
// 導入模塊(需要在node_modules目錄下安裝yamljs模塊)
const YAML = require('yamljs')
// 解析YAML字符串
const yl = YAML.parse(yaml)
// 查看內容
cy.log(yl)
})
});
it('讀取圖片 ', function () {
cy.readFile('sss.png','base64').then(function (png) {
cy.log(png)
})
});
it('讀取mp3', function () {
cy.readFile('sss.mp3','base64').then(function (mp3) {
const uri='data:audio/mp3;base64,'+mp3
const audio=new Audio(uri)
audio.play()
})
});
it('讀取mp4文件', function () {
cy.readFile('sss.mp4','base64').then(function (mp4) {
cy.log(mp4)
})
});
});
.writeFile()
寫入具體制定內容的文件
基本語法格式
cy.writeFile(filePath, contents)
cy.writeFile(filePath, contents, encoding)
cy.writeFile(filePath, contents, options)
相關參數
- filePath 項目根目錄(包含默認cypress.json配置文件的目錄)中需要讀取的文件路徑
- encoding 讀取時需要使用的編碼
- ascii
- base64
- binary
- hex
- latin1
- utf8
- utf-8
- ucs2
- ucs-2
- utf16le
- utf-16le
- options
- log 是否將命令顯示到命令日志中,默認true
- flag 文件系統標志,默認w
- encoding 寫入文件時要使用的編碼,默認utf8
flag文件系統詳細標志
選項 | 描述 |
---|---|
a | 在結尾進行追加,但不可讀,如果文件不存在,則創建該文件 |
ax | 和a一樣,但如果路徑存在則失敗 |
a+ | 可讀,也可以在結尾處進行追加,如果文件不存在,則創建該文件 |
ax+ | 和a+一樣,但如果路徑存在則失敗 |
as | 以同步方式添加,但不可讀,如果文件不存在,則創建該文件 |
as+ | 可讀,也可以同步方式添加,如果文件不存在,則創建該文件 |
r | 只讀,如果文件不存在,則會發生異常 |
r+ | 可讀寫,如果文件不存在,則會發生異常 |
rs+ | 以同步方式進行讀寫,操作系統繞過本地文件系統緩存(不推薦) |
w | 只寫,如果文件不存在則創建,文件存在則覆蓋內容 |
wx | 和w一樣,但如果路徑存在則失敗 |
w+ | 可讀寫,如果文件不存在則創建愛你,文件存在則覆蓋內容 |
wx+ | 和w+一樣,但如果路徑存在則失敗 |
操作實例代碼
describe('測試寫入文件', function () {
it('寫入txt文件', function () {
cy.writeFile('D:/cypresses/uuu.txt','hello world1')
});
it('寫入json文件', function () {
cy.writeFile('D:/cypresses/uuu.json',{'age':18})
});
it('寫入json文件,a+模式', function () {
cy.writeFile('D:/cypresses/uuu.json',{'name':'wupeng'},{flag:'a+'})
});
});
其他命令
.wrap()
作用
- 返回傳遞給它的對象
- 返回的是一個Promise對象,可以直接接Cypress其他命令
- 如果傳遞給它的就是一個Promise對象,則返回它的值
基本語法格式
// subject表示需要返回的對象
// options有兩個,log:是否將命令系那是到命令日志中,默認true;timeout:命令超時時間
cy.wrap(subject)
cy.wrap(subject, options)
基本實例
// 聲明一個整數
cy.wrap(123).should('eq', 123)
// 聲明一個字符串
cy.wrap('abc').and('contain', 'a')
最佳實踐
設置全局URL
- 為了繞過同源策略,當 Cypress 開始運行測試時,會在 localhost 上打開一個隨機端口進行初始化
- 直到遇見第一個 cy.visit() 命令里的 URL 才匹配被測應用程序的 URL
- 當 Cypress 以交互模式啟動時,會看到 Cypress 先運行在 localhost 上然后又切換到 URL 重新運行(也就多消耗了時間)
- 在cypress.json中設置baseUrl
- 可以在運行時節省 Cypress 匹配被測應用程序 URl 的時間
- 還可以在編寫待訪問的 URL 時,忽略 baseUrl,直接寫后面的路徑
設置全局實例
// 不加 baseUrl 的寫法
cy.visit('https://example.cypress.io/commands/actions')
// 加了上面 baseUrl 的寫法
cy.visit('/commands/actions')
避免訪問多個站點
- 為了繞開同源策略的限制而實現的方案,使得 Cypress 不能支持在一個測試用例文件里訪問多個不同域名的 URL
- 如果訪問了多個不同域名的站點,Cypress 會直接報錯
# 避免訪問多個站點基本實例
// 訪問相同超域(如果訪問的是同一個超域下的不同子域,則 Cypress 允許你正常訪問)
it('訪問同一超域下的不同子域', function () {
cy.visit('https://example.cypress.io')
cy.visit('https://www.cypress.io/features')
});
// 訪問不同超域(如果訪問的不是同一個超域下的子域,則 Cypress 不允許你正常訪問)
it('訪問不同超域,會報錯', function () {
cy.visit('https://example.cypress.io')
cy.visit('https://www.cnblogs.com/wp950416/p/13915633.html')
});
刪除等待代碼
- 在其他的自動化測試框架中,很大概率會用到強制等待及隱式等待
- 在cypress中,無需使用等待,Cypress的許多命令都自帶自動重試機制(所以在編寫腳本的時候,刪除所有的等待代碼)
截圖與錄屏
在測試運行時截圖和錄屏能夠在測試錯誤時快速定位到問題所在
以cypress run方式運行測試時,當測試發生錯誤時,Cypress會自動截圖,並默認保存在cypress/screenshots文件夾下,而錄屏會保存在cypress/video文件夾下
自定義截圖基本語法格式
.screenshot()
.screenshot(fileName)
.screenshot(options)
.screenshot(fileName, options)
// ---or---
cy.screenshot()
cy.screenshot(fileName)
cy.screenshot(options)
cy.screenshot(fileName, options)
參數解釋
- fileName
- 代保存圖片的名稱
- 圖片默認保存在 cypress/screenshots 文件夾下,可以在 cypress.json 修改默認文件夾路徑(配置項 screenshotsFolder )
- options
參數 | 默認 | 作用 |
---|---|---|
log | true | 在命令行中顯示 |
blackout | [ ] | 接收一個數組類型的字符選擇器,此選擇器匹配的元素會被塗黑 這個選項在capture是runner時不起作用 |
capture | fullPage | 決定截圖截取測試運行器的哪個部分 此參數僅限在cy.后使用游戲哦啊,針對某個元素截圖則不起作用 有三個可選項: 1. viewport:截圖大小是被測應用程序當前視窗大小 2. fullpage:整個被測程序界面(整個頁面) 3. runner:截圖測試運行器的整個部分 |
clip | null | 用於裁剪最終屏幕截圖圖像的位置和尺寸(以px為單位) 格式:{x:0,y:0,width:100,height:100} |
disableTimersAndAnimations | true | 截圖時禁止JS計時器(setTimeout、setInterval)和CSS動畫運行 |
padding | null | 用於更改元素屏幕截圖尺寸的填充 僅適用於元素屏幕截圖 格式:padding:[ 'background-color':'#fff'; ] |
scale | false | 是否縮放應用程序以適合瀏覽器窗口 當capture=runner時,強制為TRUE |
timeout | responseTimeout | Timeout時間 |
onBeforeScreenshot | null | 非因測試失敗截圖前,要執行的回調函數 當此參數應用於元素屏幕截圖時,它的參數是被截取的元素 當此參數應用於其他截圖時,它的參數是document本身 |
onAfterScreenshot | null | 非因測試失敗截圖后,要執行的回調函數 當此參數應用於元素屏幕截圖時,第一個參數是被截取的元素 當此參數應用於其他截圖時,第一個參數是document本身,第二個參數是有關屏幕截圖的屬性 |
通過onBeforeScreenshot、onAfterScreenshot.可以在截圖發生前或發生后應用自定義的行為
基本實例
it('簡單的栗子', function () {
// 截圖整個頁面
cy.screenshot()
});
斷言最佳實踐
介紹
- Cypress的斷言庫是基於Chai斷言庫
- 並且增加了對 Sinon-Chai,Chai-jQuery 斷言庫的支持,帶來了強大的斷言功能
- Cypress 支持 BDD(expect/should)和 TDD(assert)格式的斷言
BDD、TDD格式斷言實例
# BDD斷言案例
describe('測試BDD', function () {
context('BDD', function () {
it('BDD', function () {
function add(a,b){
return a+b
}
expect(add(1,2)).to.eq(3)
});
})
});
# TDD斷言案例
describe('測試BDD', function () {
context('BDD', function () {
it('BDD', function () {
function add(a,b){
return a+b
}
assert.equal(add(1,3),4,'相加等於4')
});
})
});
BDD形式的常見斷言
命令 | 示例 |
---|---|
not | expect(name).to.not.equal('jane') |
deep | expect(obj).to.deep.equal({name:'jane'}) |
nested | expect({a:{b:['x','y']}}).to.have.nested.property('a.b[1]') expect({a:{b:['x','y']}}).to.nested.include({'a.b[1]':'y'}) |
any | expect(arr).to.have.any.keys('age') |
all | expect(arr).to.have.all.keys('name','age') |
a(type) 別名:an |
expect('test').to.be.a('string') |
include(value) 別名:contain,includes,contains |
expect([1,2,3]).to.include(2) |
ok | expect(undefined).to.not.be.ok |
true | expect(true).to.be.true |
false | expect(false).to.be.false |
null | expect(null).to.be.null |
undefined | expect(undefind).to.be.undefind |
exist | expect(myVar).to.exist |
empty | expect([]).to.be.empty |
arguments 別名:Arguments |
expect(arguments).to.be.arguments |
equal(value) 別名:equals,eq |
expect(42).to.equal(42) |
deep.equal(value) | expect({name:'jane'}).to.deep.equal({name:'jane'}) |
eql(value) 別名:eqls |
expect({name:'jane'}).to.eql({name:'jane'}) |
greaterThan(value) 別名:gt,above |
expect(10).to.be.greaterThan(5) |
least(value) 別名:gte |
expect(10).to.be.at.least(10) |
lessThan(value) 別名:lt,below |
expect(5).to.be.lessThan(10) |
most(value) | expect('test').to.have.length.of.at.most(4) |
within(start,finish) | expect(7).to.be.within(5,10) |
instanceOf(constructor) 別名:instanceof |
expect([1,2,3]).to.be.instanceOf(Array) |
property(name,[value]) | expect(obj).to.have.property('name') |
deep.property(name,[value]) | expect(deepObj).to.have.deep.property('tests[1]','e2e') |
ownProperty(name) 別名:haveOwnProperty,own.property |
expect('test').to.have.ownProperty('length') |
ownPropertyDescriptor(name) 別名:haveOwnPropertyDescriptor |
expect({a:1}).to.have.ownPropertyDescriptor('a') |
lengthOf(value) | expect('test').to.have.lengthOf(3) |
match(RegExp) 別名:matches |
expect('testing').to.match(/^test/) |
string(string) | expect('testing').to.have.string('test') |
keys(key1,[key2],[...]) 別名:key |
expect({pass:1,fail:2}).to.have.keys('pass','fail') |
TDD形式的斷言
命令 | 示例 |
---|---|
.isOk(object,[message]) | assert.isOk('everything','everything is ok') |
.isNotOk(object,[message]) | assert.isNotOk('false','this will pass') |
.equal(actual,expected,[message]) | assert.equal(3,3,'vals equal') |
.notEqual(actual,expected,[message]) | assert.notEqual(3,4,'vals not equal') |
.deepEqual(actual,expected,[message]) | assert.deepEqual({id:'1'},{id:'2'}) |
.notDeepEqual(actual,expected,[message]) | assert.notDeepEqual({id:'1'},{id:'2'}) |
.isAbove(valueToCheck,valueToBeAbove,[message]) | assert.isAbove(6,1,'6 greater than 1') |
.isAtLeast(valueToCheck,valueToBeAtLeast,[message]) | assert.isAtLeast(5,2,'5 gt or eq to 2') |
.isBelow(valueToCheck,valueToBeBelow,[message]) | assert.isBelow(3,6,'3 strict lt 6') |
.isAtMost(valueToCheck,valueToBeAtMost,[message]) | assert.isAtMost(4,4,'4 lt or eq to 4') |
.isTrue(value,[message]) | assert.isTrue(true,'this val is true') |
.isNotTrue(value,[message]) | assert.isNotTrue('tests are no fun','val not true') |
.isFalse(value,[message]) | assert.isFalse(false,'val is false') |
.isNotFalse(value,[message]) | assert.isNotFalse('tests are fun','val not false') |
.isNull(value,[message]) | assert.isNull(err,'there was no error') |
.isNotNull(value,[message]) | assert.isNotNull('hello','is not null') |
.exists(value,[message]) | assert.exists(5,'5 is not null or undefined') |
.notExists(value,[message]) | assert.notExists(null,'val is null or undefined') |
.isUndefined(value,[message]) | assert.isUndefined(undefined,'val has been defined') |
.isDefined(value,[message]) | assert.isDefined('hello','val has been defined') |
.isFunction(value,[message]) | assert.isFunction(x=>x*x,'val is func') |
.isnNotFunction(value,[message]) | assert.isNotFunction(5,'val not funct) |
.isObject(value,[message]) | assert.isObject({num:5},'val is object') |
.isNotObject(value,[message]) | assert.isNotObject(3,'val not object') |
.isArray(value,[message]) | assert.isArray(['unit','e2e'],'val is array') |
.isNotArray(value,[message]) | assert.isNotArray('e2e','val not array') |
.isString(value,[message]) | assert.isString('e2e','val is string') |
.isNotString(value,[message]) | assert.isNotString(2,'val not string') |
.isNumber(value,[message]) | assert.isNumber(2,'val is number') |
.isNotNumber(value,[message]) | assert.isNotNumber('e2e','val not number') |
.isBoolean(value,[message]) | assert.isBoolean(true,'val is bool') |
.isNotBoolean(value,[message]) | assert.isNotBoolean('true','val not bool') |
.typeOf(value,name,[message]) | assert.typeOf('e2e','string','val is string') |
.notTypeOf(value,name,[message]) | assert.notTypeOf('e2e','number','val not number') |
Cypress命令內置斷言實例
Cypress命令通常具有內置的斷言,這些斷言將導致命令自動重試,以確保命令成功(或者超時后失敗)
it('cypress 命令自帶斷言', function () {
cy.wrap({body: {name: 'sunfree'}})
.its('body')
.should('deep.eq', {name: 'sunfree'})
});
Cypress常見的內置斷言操作命令
命令 | 內置斷言事件 |
---|---|
cy.visit() | 期望訪問的URL返回的status code是200 |
cy.request() | 期望遠程server存在並且能連通 |
cy.contains() | 期望包含某些字符的頁面元素能在DOM里找到 |
cy.get() | 期望頁面元素能在DOM里找到 |
cy.type() | 期望頁面元素處於可輸入的狀態 |
cy.click() | 期望頁面元素處於可點擊的狀態 |
cy.its() | 期望能從當前對象找到一個屬性 |
隱性斷言
- should()、and()是Cypress推崇的方式
- and()和should()其實是使用方式和效果是完全一樣的
cy
.get('form')
.should('be.visible')
.and('have.class', 'open')
顯性斷言
expect允許傳入一個特定的對象並且對它進行斷言
expect(true).to.be.true
po模式(不推薦使用)
數據驅動策略
通過js的方式創建
具體操作,詳情點開動態生成測試用例一欄。
使用fixture(推薦)
參考.fixture()一欄。
數據保存到自定義文件中
第一步:創建自定義json文件放入測試套件的data目錄下,並編寫需要數據
[
{
"username": "admin",
"password": "123456"
},
{
"username": "admin",
"password": "1123456"
}
]
第二步:編寫測試用例(需要先引入外部的數據文件)
import data from "../integration/data/te.json"
describe('登錄', function () {
beforeEach(function () {
cy.visit("http://192.168.102.49:5001/#/")
});
// 循環測試數據
for (const datas of data) {
it("sss", function () {
cy.get("#username").type(datas.username)
cy.get("#password").type(datas.password)
cy.get("button[type=submit]").click()
});
}
});
環境變量設置指南
- 在實際項目可能存在多個環境(開發、測試、預發、生產),不同環境的環境變量就會不一樣。
- 如果還是單純只用上面講到的方式,切換不同環境時,還得手動修改環境便令,及其不方便。
第一種解決方案
- 在cypress安裝目錄下創建愛你一個config文件夾
- 文件夾下建立兩個文件,分別命令為cypress.dev.json,cypress.qa.json
- 在cypress安裝目錄/plugins/index.js中更改配置
- 命令行輸入npm run cypress:open --env configFile=qa
// 第一步、第二步(完成)
// 第三步,更改配置
const fs = require("fs-extra")
const path = require("path")
/**
* @type {Cypress.PluginConfig}
*/
function getConfigurationByFile(file) {
const pathToConfigFile = path.resolve("..", "cypresses/cypress/config", `cypress.${file}.json`)
console.log(pathToConfigFile)
return fs.readJson(pathToConfigFile)
}
module.exports = (on, config) => {
const file = config.env.configFile = "qa" || "dev"
return getConfigurationByFile(file)
}
// 第四步,新建測試用例
describe('測試環境變量設置', function () {
it('開始測試', function () {
cy.log(`${Cypress.env('username')}`)
cy.log(`${Cypress.env('password')}`)
});
});
第二種解決方案
- cypress.json添加內容
- 在support/index.js中添加代碼
- 輸入命令行:npm run cypress:open --env testEnv=qa
第一步:
{
"targetEnv": "dev",
"env": {
"dev": {
"username": "admin",
"password": "123456",
"Url": "http://192.168.102.49:5001/#/"
},
"qa": {
"username": "admin",
"password": "123456",
"Url": "http://192.168.102.49:5001/#/"
}
}
}
第二步:
beforeEach(function () {
const targetEnv=Cypress.env('targetEnv')||Cypress.config('targetEnv')
cy.log(`${JSON.stringify(Cypress.env(targetEnv).username)}`)
});
接口測試網絡相關命令
.request()
用來發送HTTP請求
基本語法格式
cy.request(url)
cy.request(url, body)
cy.request(method, url)
cy.request(method, url, body)
cy.request(options)
相關參數說明
- url 用來請求url
- body
- 請求正文,不同接口內容,body會有不同的形式。
- Cypress設置了Accepts請求頭,並通過encoding選項序列化響應體。
- method 請求方法,默認是get
- options
參數 | 默認 | 作用 |
---|---|---|
log | true | 在命令日志中顯示命令 |
url | null | 請求url |
method | get | 請求方法 |
auth | null | 添加鑒權標頭 |
body | null | 請求體 |
failOnStatusCode | true | 返回值不是2xx或3xx時,是否直接返回失敗 |
followRedirect | true | 是否自動重定向 |
form | false | 是否將body的值轉換為rul-encoded並設置x-www-form-urlencoded標頭(表單格式) |
encoding | utf8 | 1. 徐麗華響應體時要用的編碼 2. 支持:ascli,base64,binary,hex,latin1,utf8,utf-8,ucs2,ucs-2,utf16le,utf-16le |
gzip | true | 是否接受gzip編碼 |
headers | null | 要發送的額外請求頭 |
qs | null | params把查詢參數追加到請求url后 |
retryOnStatusCodeFailure | false | StatusCode失敗后是否自動重試,若ture則自動重試4次 |
retryOnNetworkFailure | ture | 網絡問題導致失敗后是否自動重試,若true則自動重試4次 |
代碼實例
describe('接口測試', function () {
beforeEach(function () {
cy.visit("https://api.apiopen.top")
});
it('測試request', function () {
cy.request({
method:"get",
url:'/getSingleJoke',
qs:{
sid: "28654780"
}
}).as("comments")
cy.get("@comments").then(function (response) {
expect(response.status).to.be.eq(200)
})
});
});
注意事項
- 通過.request()發出的請求不會出現在開發者工具,開發人員工具中看不到該請求。
- cypress實際上並未從瀏覽器發出XHR請求
- 實際上是從Cypress Test Runner發出HTTP請求
關於cookie
- 通過 .request()發出的請求,Cypress會自動發送和接收Cookie
- 在發送 HTTP 請求之前,如果請求來自瀏覽器,Cypress會自動附加本應附加的 Cookie
- 此外,如果響應具有 Set-Cookie 標頭,則這些標頭將自動在瀏覽器Cookie上重新設置
- 換句話說,cy.request()透明地執行所有基礎功能,就好像它來自瀏覽器一樣
.route()
用來管理控制整個網絡請求
前言
- cypress6.0開始不推薦使用cy.server()和cy.route()
- 在將來的版本中,對cy.server()和cy.route()
- 現在有限考慮使用cy.intercept()
注意事項
- cypress目前支持攔截XMLHttpRequest,可在開發者工具看到請求type是xhr,或者直接點擊xhr進行篩選。
- 同樣是login請求,有些是xhr,有些卻是document,對於type=document的請求,.route()默認不會攔截到的。
基本語法格式
cy.route(url)
cy.route(url, response)
cy.route(method, url)
cy.route(method, url, response)
cy.route(callbackFn)
cy.route(options)
參數說明
- url 需要監聽的URL,遵循minimatch模式。
- response 為匹配上的URL提供自定義響應體
- method 待匹配監聽URL的請求方法
- callbackFn 回調函數
- options 可選項(具體如下表格)
參數 | 默認 | 作用 |
---|---|---|
delay | 0 | 設定stubbed返回的延遲時間,ms為單位 |
force404 | false | 當XHR請求沒有匹配到任何cy.route()定義的時候,強制返回404 |
headers | null | 設定stubbed路由的header |
method | GET | 設定待匹配的請求方法,默認GET |
onAbort | null | 回調函數,當一個XHR被終止的時候被調用 |
onRequest | null | 設定請求被發送時的回調函數 |
onResponse | null | 設定服務器返回時的回調函數 |
response | null | 設定stubbed路由的返回響應體 |
status | 200 | 設定stubbed路由的返回的狀態碼 |
url | null | 匹配請求url的字符串或者正則表達式 |
使用總結
- 當發出請求的url+method匹配上路由的url+method,就會被該路由監聽到
- 簡單理解:response 是自定義響應體,status是自定義響應狀態碼,headers是自定義響應頭
- 如果設置了response、status、headers 參數,則被監聽到的請求會獲取到這三個參數
基本操作
例一:
cy.server()
cy.route('**/users/*/comments')
// https://localhost:7777/users/123/comments <-- 匹配
// https://localhost:7777/users/123/comments/465 <-- 不匹配
例二:
cy.server()
cy.route('**/posts/**')
// https://localhost:7777/posts/1 <-- 匹配
// https://localhost:7777/posts/foo/bar/baz <-- 匹配
// https://localhost:7777/posts/quuz?a=b&1=2 <-- 匹配
// https://localhost:7777/posts <-- 不匹配
例三:
cy.route('**/users/*')
// 下面的都匹配
/users/1
http://localhost:2020/users/2
https://google.com/users/3
// 下面的都不匹配
/users/4/foo
http://localhost:2020/users/5/foo
基本實例代碼
const username = 'jane.lane'
const password = 'password123'
before(function () {
cy.visit('http://localhost:7079/')
})
it('正常登錄,修改登錄請求的status、response', function () {
cy.server()
cy.route({
url: '**/login',
method: 'POST',
status: 503,
delay: 1000,
response: {
success: false,
data: 'Not success'
},
}).as("login")
cy.get("input[name=username]").type(username)
cy.get("input[name=password]").type(`${password}{enter}`)
cy.wait('@login').then((res) => {
cy.log(res)
expect(res.status).to.eq(503)
expect(res.responseBody.data).to.eq('Not success')
})
});
路由結果日志
- 每當啟動服務器(cy.server())並添加路由(cy.route())時,Cypress都會顯示一個名為ROUTES(n)的新模塊日志。
- 將在日志中列出路由表,包括方法,URL,是否Stubbed,別名和成功匹配請求的數量。
- 當發出XHR請求后,cypress會記錄此請求是否匹配到某個路由的別名。
強制返回404實例
cy.server({ force404: true })
cy.route({
url: '**/logins',
method: 'POST',
status: 503,
delay: 1000,
response: {
success: false,
data: 'Not success'
},
}).as("login")
// 偽代碼
// 發出 /login 請求的操作
當/login沒有匹配到任意路由的時候,會返回404
官方實例
it('cy.route() - route responses to matching requests', () => {
// https://on.cypress.io/route
// 訪問
cy.visit('https://example.cypress.io/commands/network-requests')
// 預置變量
let message = 'whoa, this comment does not exist'
// 啟動 Mock 服務器
cy.server()
// 路由1:監聽 url 是 comments/* 且 請求方法是 GET 的請求
cy.route('GET', 'comments/*').as('getComment')
// 點擊按鈕觸發請求
cy.get('.network-btn').click()
// 等待請求響應成功后獲取 status 進行斷言
cy.wait('@getComment').its('status').should('eq', 200)
// 路由2:監聽 url 是 /commets 且 請求方法是 POST 的請求
cy.route('POST', '/comments').as('postComment')
// 點擊按鈕觸發請求
cy.get('.network-post').click()
// 等待請求響應成功后進行斷言
cy.wait('@postComment').should((xhr) => {
expect(xhr.requestBody).to.include('email')
expect(xhr.requestHeaders).to.have.property('Content-Type')
expect(xhr.responseBody).to.have.property('name', 'Using POST in cy.route()')
})
/*
路由3:監聽 url 是 comments/* 且 請求方法是 POST 的請求
自定義 status、response、delay 並返回給監聽到的請求
*/
cy.route({
method: 'PUT',
url: 'comments/*',
status: 503,
response: {error: message},
delay: 500,
}).as('putComment')
// // 等待請求響應成功后進行斷言
cy.get('.network-put').click()
cy.wait('@putComment')
// 出現 404 之后斷言文案
cy.get('.network-put-comment').should('contain', message)
})
.server()
啟動服務器以開始講響應路由到cy.route()並更改網絡請求的行為。cyprss提供的mock方案,使用自帶的命令cy.server()、cy.route(),可以進行接口測試,還可以截獲、控制及修改接口返回的行為。
基本語法格式
cy.server()
cy.server(options)
options參數
用於控制服務器,將會影響所有請求的行為。
參數 | 默認 | 作用 |
---|---|---|
enable | true | 若為false,則表明禁用已有的route stubs |
force404 | false | 當XHR請求沒有匹配到任何cy.route()定義的時候,強制返回404 |
onAnyAbort | undefined | 回調函數,當一個XHR被中止的時候被調用 |
onAnyRequest | undefined | 設定請求被發送時的回調函數 |
onAnyResponse | undefined | 設定服務端返回時的回調函數 |
urlMatchingOptions | {matchBase:true} | 使用全局字符串匹配URL時,傳遞給minimatch的默認選項 |
ignore | function | 1. 回調功能,可將被logged或stubbed的請求列入白名單 2. 默認匹配:.js、.html、.css文件 |
不帶參數實例
- 任何與cy.route()不匹配的請求都講傳遞到服務器,除非設置了force404,這樣請求變成404和拿到一個空response
- 與options.ignore函數匹配的任何請求都不會記錄或存根(logged、stubbed)
- 將在命令日志中看到名為(XHR Stub)或(XHR Stub)或(XHR)的請求。
帶參數實例
context('route 的栗子', function () {
const username = 'jane.lane'
const password = 'password123'
before(function () {
cy.visit('http://localhost:7079/')
})
it('栗子1', function () {
cy.server({
method: 'POST',
status: 503,
delay: 1000,
headers: {
'x-token': 'abc-123-foo-bar'
}
})
cy.route({
url: '**/login',
response: {
success: false,
data: 'Not success'
},
}).as("login")
cy.get("input[name=username]").type(username)
cy.get("input[name=password]").type(`${password}{enter}`)
cy.wait('@login').then((res) => {
cy.log(res)
expect(res.status).to.eq(503)
expect(res.responseBody.data).to.eq('Not success')
expect(res.responseHeaders).to.have.property('x-token', 'abc-123-foo-bar')
})
});
})
啟動服務器,關閉服務器實例
it('栗子2', function () {
cy.server()
cy.route({
url: '**/login',
method: 'POST',
status: 503,
response: {
data:"success"
}
}).as("login")
cy.get("input[name=username]").type(username)
//第一次發出請求
cy.get("input[name=password]").type(`${password}{enter}`)
cy.wait('@login').then((res) => {
expect(res.status).to.eq(503)
// 關閉服務器
cy.server({
enable: false
})
})
cy.visit('http://localhost:7079/')
cy.get("input[name=username]").type(username)
//第二次發出請求
cy.get("input[name=password]").type(`${password}{enter}`)
});
// 第二個請求雖然被路由監聽到了,但是因為服務器關閉了,所有並沒有獲取路由的status、response
注意事項
- 可以在啟動cy.visi()之前啟動服務器cy.server()
- 應用程序在加載時可能會立即發出初始請求(例如對用戶進行身份驗證)
- cypress可以在cy.visit()之前啟動服務器並定義路由(cy.route())
- 下次訪問時,服務器+路由將在應用程序加載之前立即應用。
.intercept()
使用該命令在網絡層管理HTTP請求的行為,在Cypress6.0之后才支持該方法。
具體功能
- 對任何類型的HTTP請求進行stub或spy
- 在HTTP請求發送到目標服務器前,可以修改HTTP請求body、headers、URL(類似抓包工具對請求進行打斷點然后修改)
- 動態或靜態的對HTTP請求的相應進行stub
- 接收HTTP相應后可對HTTP響應body、headers、status、code進行修改(類似抓包工具對響應進行打斷點,然后修改)
- 在所有階段都可以完全訪問所有HTTP請求
對比cy.route()
- 可以攔截所有類型的網絡請求,包括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,可以是字符串也可以是正則表達式。
method
請求方法
routeMatcher
- 一個對象,用於匹配此路由將處理那些傳入的HTTP請求
- 所有對象屬性都是可選的,不是必填的
- 設置的所有屬性必須與路由匹配才能處理請求
- 如果將字符串傳遞給任何屬性,則將使用minimatch將與請求進行全局匹配
routeMatcher如下屬性
{
/**
* 與 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
}
routeHander
- 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**
1. 如果傳遞一個字符串,這個值相當於響應body的值
2. 等價於StaticResponse對象{body:"dev"}
**object**
1. 如果傳遞了沒有StaticResponse秘鑰的對象,則它將作為JSON響應Body發送
2. 例:{dev:"say"}等價於StaticResponse對象{body:{dev:"say}}
**function**
1. 如果傳遞了一個回調函數,當一個請求匹配上了該路由將會自動調用這個函數
2. 函數第一個參數是請求對象
3. 在回調函數內部,可以修改外發請求、發送響應、訪問實際響應
**命令返回結果**
1. 返回null
2. 可以連接as()進行別名,但不可鏈接其他命令
3. 可以使用cy.wait()和等待cy.intercept()路由匹配上請求,這將會產生一個獨享,包含匹配上的請求/響應相關信息。
通過URL路由匹配請求實例
describe('發送請求', function () {
it('should ', function () {
cy.visit("http://192.168.102.49:5001")
cy.intercept("http://192.168.102.49:5001").as('login')
cy.login("admin","123456")
cy.wait('@login').then(function (result) {
cy.log(result)
})
});
});
通過RouteMatcher路由匹配實例
describe('通過routeMatcher發送請求', function () {
it('should ', function () {
cy.visit("http://192.168.102.49:5001")
cy.intercept({
hostname:"192.168.102.49",
pathname:'',
method:'post',
https:false
}).as('login2')
cy.login("admin","123456")
cy.wait("@login2").then(function (result) {
cy.log(result)
expect(result.response.body).to.have.property('code')
expect(result.response.statusCode).to.be.eq(200)
})
});
});
自定義不同類型響應體實例
describe('自定義響應內容', function () {
beforeEach(function () {
cy.visit("http://192.168.102.49:5001/#/")
});
it('自定義字符串響應體 ', function () {
cy.intercept("/login", 'successes').as('login_str')
cy.login("admin", "123456")
cy.wait("@login_str").then(function (str) {
cy.log(str)
})
});
it('自定義json響應體', function () {
cy.intercept("/login", {fixture: 'test.json'}).as("login_json")
cy.login("admin", "123456")
cy.wait("@login_json").then(function (to_json) {
cy.log(to_json)
})
});
it('自定義一個StaticResponse響應體', function () {
cy.intercept("/login",{
body:{
"name":"wujuan"
},
statusCode:201,
delayMs: 2000
}).as("login_sr")
cy.login("admin", "123456")
cy.wait("@login_sr").then(function (sr) {
cy.log(sr)
})
});
});
自定義攔截請求實例
后續補充。。。。
后續補充。。。。
后續補充。。。。
后續補充。。。。
后續補充。。。。
后續補充。。。。
接口測試操作cookie命令
.getCookies()、.getCookie()
獲取所有的cookie,返回一個cookie對象數組。如果只是獲取單個cookie,需要添加name值(必傳)。
基本語法
cy.getCookies(name)
cy.getCookies(name,options)
option參數
- log:是否將命令顯示到命令日志中,默認true
- timeout:命令超時時間
基本代碼實例
context('登錄測試', function () {
const username = 'wupeng'
const password = 'bb961202'
beforeEach(function () {
cy.visit('http://192.168.102.210:10001/zentao/user-login.html')
cy.get('input[name=account]').type(username)
cy.get('input[name=password]').type(password)
cy.get('#submit').click()
})
it('getCookie', function () {
cy.getCookies().each(function (cookie) {
cy.log(cookie)
})
});
})
.setCookie()
基本語法格式
cy.setCookie(name, value)
cy.setCookie(name, value, options)
基本參數
- name cookie的名稱
- value cookie的值
- options可選值
option | 默認值 | 描述 |
---|---|---|
log | true | 命令是否顯示在命令日志中 |
domain | window.location.hostname | cookie設置在哪個域下 |
expiry | 20 years into the future | cookie到期時間 |
httpOnly | false | 是否是僅僅HTTP cookie |
path | / | cookie路徑 |
secure | false | 是否為安全Cookie |
timeout | responseTimeout | 命令超時時間 |
sameSite | undefined | 1. 如果設置,則應為lax,strict或no_restriction之一。 2. 傳遞undefined以使用瀏覽器的默認值。 3. 注意:僅當安全標志設置為true時,才能使用no_restriction |
基本用法
cy.setCookie('auth_key', '123key')
命令返回結果
返回設置的cookie對象並且包含以下屬性
- domain
- expiry(如果有)
- httpOnly
- name
- path
- sameSite(如果有)
- secure
- value
.clearCookies()、.clearCookie()
cypress會在每次測試之前自動清除所有Cookie,以防止在測試用例之間共享狀態,除非在測試用例中需要調用此命令清除某個Cookie,否則不要使用該命令。如果清除的不是所有,需要穿cookie的name值。
基本語法格式
cy.clearCookie(name)
cy.clearCookie(name, options)
option參數
- log 是否將命令顯示到命令日志中,默認是true
- timeout 命令超時時間
基本代碼實例
context('登錄測試', function () {
const username = 'wupeng'
const password = 'bb961202'
beforeEach(function () {
cy.visit('http://192.168.102.210:10001/zentao/user-login.html')
cy.get('input[name=account]').type(username)
cy.get('input[name=password]').type(password)
cy.get('#submit').click()
})
it('getCookie', function () {
cy.clearCookies()
cy.getCookies().each(function (cookie) {
cy.log(cookie)
})
});
})
// 先清除cookie之后,在進行獲取,值就為空了。
Cypress API相關
Cypress.Commands
介紹
- Custom Commands 被認為是替代 PageObject 的良好選擇
- 使用 Custom Commands 可以創建自定義命令和替換現有命令
- Custom Commands 默認存放在 cypress/support/commands.js 文件中,因為它是通過 supportFile( 定義在 cypress/support/index.js )中的 import 語句導入的,所以會在所有測試用例執行前加載
基本語法格式
Cypress.Commands.add(name, callbackFn)
Cypress.Commands.add(name, options, callbackFn)
Cypress.Commands.overwrite(name, callbackFn)
# 參數說明
name:要添加或覆蓋的命令的名稱
cakkbackFn:自定義命令的回調函數,回調函數里自定義函數所需完成的操作步驟
options:允許自定義命令的隱性行為
# options可選參數列表
參數:prevSubject
可接受的值類型:Boolean,String or Array
1. false:忽略任何以前命令產生的對象(父命令)默認:false
2. true:接收上一個命令產生的對象(子命令)
3. optional:可以啟動鏈,也可以使用現有鏈(雙命令,除了控制命令的隱式行為,還可以對上一條命令產生的對象類型進行驗證,例如:
4. element:要求上一條命令產生的對象是DOM元素
5. document:要求上一條命令產生的對象為文檔
6. window:要求上一條命令產生的對象是窗口
描述:如何處理上一條命令產生的對象
注意:僅在Cypress.Commands.add()中支持使用 options,而在Cypress.Commands.overwrite()中不支持使用options
正確用法
Cypress.Commands.add('login', (email, pw) => {})
Cypress.Commands.overwrite('visit', (orig, url, options) => {})
基本代碼實例
第一步:編寫commands.js的內容
Cypress.Commands.add('login', function (username, password) {
cy.visit("http://192.168.102.49:5001")
cy.get("#username").type(username)
cy.get("#password").type(password)
cy.get("button[type=submit]").click()
})
第二步:編寫測試用例代碼
describe('測試commands', function () {
const username='admin'
const password='123456'
beforeEach(function () {
cy.login(username,password)
});
it('用來測試斷言是否可行', function () {
cy.url().should('include','/stationManage')
});
});
重寫visit實例代碼
// 第一個參數代表需要覆蓋的命令
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
const domain = Cypress.env('BASE_DOMAIN')
if (domain === '...') {
url = '...'
}
if (options.something === 'else') {
url = '...'
}
// originalFn 代表傳入進來的原 visit 命令
//
// 記得需要在最后 return
return originalFn(url, options)
})
重寫type實例代碼
// 第一步:commands.js編寫方法
Cypress.Commands.overwrite('type', (originalFn, element, text, options) => {
if (options && options.sensitive) {
options.log = false
// 創建自定義命令的日志
Cypress.log({
$el: element,
name: 'type',
message: '*'.repeat(text.length),
})
}
return originalFn(element, text, options)
})
// 第二步:編寫測試用例
cy.get('input[name=username]').type(username)
cy.get('input[name=password]').type(pwd, {sensitive: true})
使用Customn Commands的益處
- 定義在 cypress/support/command.js 中的命令可以像 Cypress 內置命令那樣直接使用,無須 import 對應的 page(實際上 PageObject 模式在 Cypress 看來無非是數據/操作函數的共享)
- 自定義命令可以比 PageObject 模式運行更快,Cypress 和應用程序運行在同一個瀏覽器中,意味着 Cypress 可以直接發送請求到應用程序並設置運行測試所需要的用戶狀態,而這一切通常無須通過頁面操作,這使得使用了自定義命令的測試會更加穩定
- 自定義命令允許重寫 Cypress 內置命令,意味着可以自定義測試框架並立刻全局應用
Cypress.Cookies
基本語法
Cypress.Cookies.debug(enable, options)
Cypress.Cookies.preserveOnce(names...)
Cypress.Cookies.defaults(options)
Cypress.Cookies.debug
- 作用 是否啟用cookie調試功能,更加易於了解cypress是如何操作cookie。
- 參數enable
- true 默認啟用,啟用后在卡覅這工具的console中可以看到詳細的cookie操作日志。
- false 不啟用,console不會顯示cookie操作日志。
- 參數options verbose:是否詳細打印cookie操作日志,默認true。
// 實例代碼一:
describe('Cypress.Cookies', function () {
it('debug', function () {
Cypress.Cookies.debug(true)
cy.setCookie('faceCookie','12345BC')
});
});
// 實例代碼二:
describe('Cypress.Cookies', function () {
it('debug', function () {
Cypress.Cookies.debug(true,{verbose:false})
cy.setCookie('faceCookie','12345BC')
});
});
// 實例代碼三:
describe('Cypress.Cookies', function () {
it('debug', function () {
Cypress.Cookies.debug(false)
cy.setCookie('faceCookie','12345BC')
});
});
Cypress.Cookies.preserveOnce
實際應用場景就是如果不保存cookie,則每次測試前都需要登錄一次,這將大大浪費不必要的測試時間。
Cypress如何保存Cookie
- Cypress.Cookies.preserveOnce()命令可以保存cookie,使它在多個測試用例間共享。
- 目前只有使用的是基於session的cookie,此命令有效。
操作實例代碼
// 第一步:編寫commands.js代碼,用於實現登錄,產生登錄的cookie.
Cypress.Commands.add('login', function (username, password) {
cy.visit("http://192.168.102.49:5001")
cy.get("#username").type(username)
cy.get("#password").type(password)
cy.get("button[type=submit]").click()
})
// 第二步:編寫測試用例
describe('保存cookie', function () {
const username="wupeng"
const password="bb961202"
before(function () {
cy.visit('http://192.168.102.210:10001/zentao/user-login.html')
cy.get('input[name=account]').type(username)
cy.get('input[name=password]').type(password)
cy.get('#submit').click()
});
beforeEach(function () {
Cypress.Cookies.preserveOnce("cypress-session-cookie")
});
it('should 1', function () {
cy.getCookies().each(function (cookie) {
cy.log(cookie.name, cookie.value)
})
});
it('should 2', function () {
cy.getCookies().each(function (cookie) {
cy.log(cookie.name, cookie.value)
})
});
});
// 兩個測試用例,主要校驗cookie是否可以共享。
Cypress.Cookies.defaults
作用
- 設置全局默認Cookie
- 可以修改全局默認值並保留一組Cookie,這些Cookie將始終在測試用例之間保留。
- 只要調用了這個方法,將在其他測試用例中都會生效。
重點 - 在cypress/support/index.js中配置此命令是絕佳選擇。
- 因為它會在所有測試文件之前加載。
可選參數
只有一個preserve參數,接收下面四種數據類型 - String
- Array
- RegExp
- Function
正確使用方式
// 所有名為 cypress-session-cookies 將不會被清除
Cypress.Cookies.defaults({
preserve: 'cypress-session-cookies'
})
// 所有名為 cypress-session-cookies 或 sessions_id 將不會被清除
// 多個 Cookie 可以用數組來存儲
Cypress.Cookies.defaults({
preserve: ['sessions_id', 'cypress-session-cookies']
})
// 滿足此正則表達式的 Cookie 將不會被清除
Cypress.Cookies.defaults({
preserve: /session|cookie/
})
Cypress.Cookies.defaults({
preserve: (cookie) => {
// 可以在這里實現自己的邏輯
// 如果函數返回 true, 那 Cookie 則不會被清除
}
})
操作代碼實例
// 第一步:編寫support/index.js
Cypress.Cookies.defaults({
preserve: /sid|session/
})
// 第二步:編寫測試用例
describe('設置全局默認cookie', function () {
const username="wupeng"
const password="bb961202"
before(function () {
cy.setCookie("session_id","test_cookie")
cy.login_zd(username,password)
});
it('should 1', function () {
cy.getCookies().each(function (cookie) {
cy.log(cookie.name, cookie.value)
})
});
it('should 2', function () {
cy.getCookies().each(function (cookie) {
cy.log(cookie.name, cookie.value)
})
});
});
總結
- 這種方式在項目中體現會有絕佳的效果。
- 一般我們需要提前知道需要的cookie是什么,但是在這里我們只需要提前在support/index.js中調用該命令去設置cookie就可以了。
Cypress.config
作用
在測試中獲取並設置配置選項
作用范圍
- 使用Cypress.config設置的配置項僅僅在當前規范文件(js測試文件)的范圍內生效。
- Cypress隔離運行每個測試文件:在一個測試文件中更改的配置在其他測試文件中不可見。
基本語法格式
Cypress.config()
Cypress.config(name)
Cypress.config(name, value)
Cypress.config(object)
相關參數
- name 要獲取或設置的配置名稱
- value 要設置的配置值
- object 使用對象屬性({}的格式)設置多個配置項。
操作實例代碼
describe('config', function () {
it('獲取配置項', function () {
// 查看所有配置
cy.log(Cypress.config())
// 設置配置項
Cypress.config('viewportWidth', 1888)
// 設置多個配置項(該操作會覆蓋原有的配置項)
Cypress.config({
'animationDistanceThreshold': 5,
'arch': "x64",
'autoOpen': false,
'baseUrl': null,
'blockHosts': null
})
});
});
Cypress.env()
作用
在測試中獲取並設置環境變量
作用范圍
- 使用Cypress.env設置的環境變量僅在當前規范文件(js測試文件)的范圍內生效
- Cypress隔離運行每個測試文件:在一個測試文件中更改的環境變量在其他測試文件中不可見
基本語法格式
Cypress.env()
Cypress.env(name)
Cypress.env(name, value)
Cypress.env(object)
相關參數
- name 要獲取或設置的環境變量名稱
- value 要設置的環境變量值
- object 使用對象屬性({}的格式)設置多個環境變量
操作實例代碼
describe('config', function () {
it('獲取配置項', function () {
// 查看所有配置
cy.log(Cypress.env())
// 設置配置項
Cypress.env('viewportWidth', 1888)
// 設置多個配置項(該操作會覆蓋原有的配置項)
Cypress.env({
'age': 5,
'sex': "x64",
})
});
});
總結
只有stop和手動關閉瀏覽器然后再次開始測試才會重置環境變量。
Cypress.dom
作用
與DOM元素相關的helper方法結合
基本語法格式
Cypress.dom.isHidden(element)
操作實例代碼
describe('設置全局默認cookie', function () {
const username = "wupeng"
const password = "bb961202"
beforeEach(function () {
cy.visit("http://192.168.102.49:5001")
});
it('測試元素是否為隱藏的', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言失敗
expect(Cypress.dom.isHidden(el)).to.be.true
})
});
it('測試元素是否附加在DOM樹上', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言成功
expect(Cypress.dom.isAttached(el)).to.be.true
})
});
it('判斷一個元素是否是另一個元素的后代', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言成功
expect(Cypress.dom.isDescendent(el.parent(),el)).to.be.true
})
});
it('判斷一個元素是否與DOM樹分離', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言失敗
expect(Cypress.dom.isdetached(el)).to.be.true
})
});
it('判斷一個元素是否是document文檔類型', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言失敗
expect(Cypress.dom.isdocument(el)).to.be.true
})
});
it('判斷一個元素是否是DOM對象', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言成功
expect(Cypress.dom.isDom(el)).to.be.true
})
});
it('判斷一個元素是否可以接收焦點', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言成功
expect(Cypress.dom.isFocusable(el)).to.be.true
})
});
it('判斷一個元素當前是否有焦點', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言失敗
expect(Cypress.dom.isFocused(el)).to.be.true
})
});
it('判斷一個元素元素是否可見', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言成功
expect(Cypress.dom.isVisible(el)).to.be.true
})
});
it('判斷一個對象是否為jQuery對象', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言成功
expect(Cypress.dom.isJquery(el)).to.be.true
})
});
it('判斷一個元素是否可滾動', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言失敗
expect(Cypress.dom.isScrollable(el)).to.be.true
})
});
it('判斷一個對象是否為Window對象', function () {
cy.get("#username").type(username).then(function (el) {
// 斷言失敗
expect(Cypress.dom.isWindow(el)).to.be.true
})
});
});
Cypress.platform
作用
- 返回基礎的操作系統名稱
- 即使Cypress在瀏覽器中運行,它也會自動使用該屬性可用於測試。
操作實例代碼
describe('cypress.platform', function () {
it('測試開始', function () {
expect(Cypress.platform).to.be.oneOf(['win32','ios'])
});
});
Cypress.version
作用
返回正在運行的Cypress的當前版本
操作實例代碼
describe('測試版本', function () {
it('測試開始', function () {
expect(Cypress.version).to.be.eq('6.5.0')
});
});
Cypress.arch
作用
返回基礎操作系統的CPU體系結構名稱
操作實例代碼
describe('測試Cpu體系結構', function () {
it('測試開始', function () {
expect(Cypress.arch).to.be.oneOf(['x64','ia32'])
});
});
Cypress.browser
作用
返回瀏覽器的屬性
屬性
選項 | 數據類型 | 描述 |
---|---|---|
channel | string | 瀏覽器發布渠道,如:stable,dev,canary |
displayName | string | 瀏覽器的可讀名稱 |
family | string | 正在使用的渲染引擎,如:chromium,firefox |
isChosen | boolean | 是否在測試運行器的瀏覽器選擇器中選擇了瀏覽器 |
majorVersion | number | 瀏覽器的主要版本號 |
name | string | 瀏覽器名稱,如:chrome,electron,firefox |
path | string | 瀏覽器在磁盤上的路徑 |
version | string | 完整的版本號 |
isHeadless | boolean | 瀏覽器是否無頭運行 |
isHeaded | boolean | 瀏覽器是否有頭運行 |
操作實例代碼
describe('測試瀏覽器屬性', function () {
it('測試開始', function () {
cy.wrap(Cypress.browser).should(function (br) {
expect(br).to.have.property('name','electron')
expect(br).to.have.property('channel','stable')
expect(br).to.have.property('isChosen',true)
expect(br).to.have.property('isHeaded',true)
})
});
});