@
Cypress介紹
Cypress是基於JavaScript語言的前端自動化測試工具,無需借助外部工具,自集成了一套完整的端到端測試方法,可以對瀏覽器中運行的所有內容進行快速、簡單、可靠的測試,並且可以進行接口測試
Cypress特點
- 時間穿梭:Cypress會在測試運行時拍攝快照。只需將鼠標懸停在命令日志上,即可清楚了解每一步都發生了什么
- 可調試性:無需揣測測試失敗原因。直接使用瀏覽器的DevTools進行調試。清晰的錯誤原因和堆棧跟蹤讓調試能夠更加快速便捷
- 實時重載:每次對測試進行更改,Cypress都會實時執行新的命令,自動重新加載頁面進行測試
- 自動等待:無需在測試中添加等待。在執行下一條命令或斷言前Cypress會自動等待元素加載完成,異步操作不再是問題
- 間諜,存根和時鍾:Cypress允許驗證並控制函數行為,Mock服務器響應或更改系統時間,更便於進行單元測試
- 網絡流量控制:Cypress可以Mock服務器返回結果,無須連接后端服務器即可實現輕松控制,模擬網絡請求
- 運行結果一致性:Cypress架構不使用Selenium或Webdriver,在運行速度、可靠性、測試結果一致性上均有良好的保障
- 截圖和視頻:Cypress在測試運行失敗時自動截圖,在使用命令運行時錄制整個測試套件的視頻,輕松掌握測試運行情況
Cypress運行原理
Cypress測試代碼和被測程序都運行在由Cypress全權控制的瀏覽器中,它們是運行在同一個域下的不同框架內,所以Cypress的測試代碼可以直接操作DOM,也正如此Cypress相對於其它測試工具可以運行的更快,在開始執行Cypress腳本后它會自動運行瀏覽器,並將編寫的代碼注入到一個空白頁,然后在瀏覽器中運行代碼
在進行接口或數據庫測試時,需要向服務端發送請求,此請求由Cypress生成,發送給Node.js Process,由Node.js轉發給服務端,因此Cypress不僅可以修改進出瀏覽器的所有內容,還可以更改可能影響自動化操作瀏覽器的代碼,所以Cypress能夠從根本上控制自動化測試的流程,提高了穩定性,使得到測試結果更加可靠,如下圖所示

Cypress安裝
-
Cypress運行需要依賴Nodejs環境,Node.js安裝很簡單,官方下載安裝即可,建議下載安裝長期維護版(LTS)
-
創建項目保存目錄,示例目錄是
D:\Code\Cypress_test\UItest
-
進入項目目錄打開cmd命令行窗口,執行命令
npm init -y
進行初始化操作,初始化后項目文件中會出現package.json
文件,此命令會讓自定義名稱、版本等信息,加上-y
參數是使用默認值,后續可在文件中修改 -
安裝Cypress,此處臨時使用了淘寶npm源,推薦使用,官方的下載太慢啦
npm install cypress --save-dev --registry=https://registry.npmmirror.com // 臨時使用淘寶npm源
也可以直接修改默認的npm源,修改命令如下
npm config set registry https://registry.npmjs.org // 設置修改配置 npm get registry // 查詢當前源配置
-
運行Cypress,每次運行都要在項目所在目錄執行命令,運行命令
npx cypress open
,運行成功會出現Cypress窗口 -
使用IDE工具打開項目目錄,默認測試用例是在
cypress/integration
下編寫,其中的兩個示例文件前期不建議刪除,供學習使用
Cypress使用
Web頁面測試
元素定位方法
Cypress更推薦使用Cypress專有選擇器,更穩定,但是需要前端代碼支持,盡管id、name、class等方法都是Cypress不推薦的,但目前元素定位還是依它們方式為主
cy.get("[data-cy=submit]").click() // Cypress專有選擇器,是Cypress推薦的,但是需要前端代碼支持
cy.get("[data-test=submit]").click() // Cypress專有選擇器
cy.get("[data-testid=submit]").click() // Cypress專有選擇器
cy.contains("Submit").click() // 通過搜索文本定位
cy.find("Submit").click() // 通過搜索文本定位
cy.get("[name=submission]").click() // 通過name定位
cy.get("#main").click() // 通過id選擇器定位
cy.get(".btn.btn-large").click() // 通過class選擇器定位
cy.get("button").click() // 通過標簽選擇器定位
cy.get("button[id=\"main\"]").click() // 通過標簽+屬性方式定位
cy.get("[data-row-key]>:nth-child(9)>:nth-child(5)").click() // 通過:nth-child()選擇器定位
還可以通過輔助方法定位元素,如下
cy.get(".btn-large").first() // 匹配找到的第一個元素
cy.get(".btn-large").last() // 匹配找到的最后一個元素
cy.get(".btn-large").children() // 獲取DOM元素的所有子元素
cy.get(".btn-large").parents() // 獲取DOM元素的所有父元素
cy.get(".btn-large").parent() // 獲取上級的第一層父元素
cy.get(".btn-large").siblings() // 獲取所有同級元素(即兄弟元素)
cy.get(".btn-large").next() // 匹配當前定位元素的下一個同級元素
cy.get(".btn-large").nextAll() // 匹配當前定位元素之后的所有同級元素
cy.get(".btn-large").nextUntil()// 匹配當前定位元素之后的所有同級元素,直到出現Until中定義的元素為止
cy.get(".btn-large").prev() // 與next()相反,匹配當前定位元素的上一個同級元素
cy.get(".btn-large").prevAll() // 與nextAll()相反,匹配當前定位元素之前的所有同級元素
cy.get(".btn-large").prevUntil()// 與nextUntil()相反,匹配當前定位元素之前的所有同級元素,直到出現Until中定義的元素為止
cy.get(".btn-large").each() // 遍歷所有子元素
也可以在Cypress運行的瀏覽器窗口定位元素,可以做參考,不推薦直接復制定位信息

元素常用操作
更多操作命令及使用方法查看官方介紹吧
cy.screenshot() // 截圖
cy.viewport(550, 750) // 設置窗口大小
cy.visit("https://www.baidu.com/") // 訪問百度
cy.visit("https://www.baidu.com/").reload() // 重新加載百度頁面
cy.go("back").go("forward") // 頁面后退、前進操作
cy.get("[type=\"text\"]").type("JavaScript") // 在當前定位元素輸入JavaScript
cy.get("[type=\"text\"]").type("123{enter}") // 在當前定位元素輸入點擊Enter鍵
cy.get("[type=\"text\"]").clear() // 清空當前定位元素的信息
cy.get("button").click() // 單擊定位元素
cy.get("button").dbclick() // 雙擊定位元素
cy.get("[type="checkbox"]").check() // 勾選全部復選框
cy.get("[type="checkbox"]").uncheck() // 取消勾選全部復選框
cy.get("[type="radio"]").first().check() // 選中單選框第一個值
cy.get("[type="radio"]").check("CN") // 選中value為CN的單選框
cy.get("#saveUserName").check() // 勾選id為saveUserName的元素
cy.get("select").select("下拉選項的值") // 下拉框選擇一個
cy.get("select").select(["value1","value2"]) // 下拉框選擇多個
cy.get("title").should("have.text","Halo").and("contain","儀表盤") // 通常使用should做斷言,它可鏈接多個斷言,更易讀,也可使用expect
cy.get("title").then(($title)=> { // ↓獲取元素對應的屬性值(即文本信息)
let Txt = $title.text() // 定義一個變量,將獲取的title信息賦值給Txt
cy.log(Txt)}) // 打印日志、打印返回結果
示例演示
新建一個js文件,編寫一個簡單的登錄腳本,然后打開Cypress窗口,點擊文件名就開始自動運行瀏覽器並進行測試啦,腳本每次修改都會自動運行,若不想運行某個用例,可以使用it.skip()
表示,只想運行某條用例則使用it.only()
表示
// halo_login.js
it("輸入正確的賬號和密碼,應登錄成功", function () {
cy.visit("/login") // 訪問路徑,baseUrl已在cypress.json文件中做配置
cy.get("[type=\"text\"]").type("admin") // 定位並輸入登錄賬號
// cy.get("[type=\"password\"]").type("admin123") // 定位並輸入登錄密碼
// cy.get(".ant-btn").click() // 點擊【登錄】按鈕
cy.get("[type=\"password\"]").type("admin123{enter}") // 輸入密碼后可通過點擊Enter鍵登錄
cy.url().should("include", "/dashboard") // 通過獲取URL地址判斷登錄成功
cy.get("title").should("have.text", "儀表盤 - Halo") // 也可通過獲取網頁標題判斷登錄成功
})
參數化測試
使用describe
命令,類似於創建了一個套件,用例在測試套件中編寫,使用forEach
遍歷數據,進而實現參數化,before
表示在測試用例運行前中執行一次
// param.js
describe("參數化測試搜索功能",function () {
before("先登錄成功",function (){ // 前置條件為登錄成功
cy.visit("http://192.166.66.24:8090/admin/index.html#/login") // 訪問路徑
cy.get("[placeholder="用戶名/郵箱"]").type("admin") // 定位並輸入登錄賬號
cy.get("[type=\"password\"]").type("admin123{enter}") // 輸入密碼后可通過點擊Enter鍵登錄
cy.visit("/posts/list") // 進入文章列表
});
["test","java","python","JavaScript"].forEach((INFO) => { // 遍歷列表中的數據
it("搜索" + INFO, () => { // 名稱為搜索與參數的組合
cy.get(".ant-form-item-children>.ant-input").type(INFO) // 獲取定位搜索框並輸入輸入參數
cy.get("[style=\"margin-right: 8px;\"]>.ant-btn").click() // 點擊【查詢】按鈕
cy.get(".ant-form-item-children>.ant-input").clear() // 每次搜索后清空輸入框
})
})
})
業務流測試
如下示例,是一個完整的業務流測試,具體步驟含義已做注釋
// halo_login.js
describe("文章管理業務流測試",function (){
before("此處是前置操作,當前模塊下執行一次!",function (){
cy.log("****** 開始測試文章管理模塊嘍! ******")
cy.visit("/login") // 訪問路徑,baseUrl已在cypress.json文件中做配置
cy.get("[type=\"text\"]").type("admin") // 定位並輸入登錄賬號
cy.get("[type=\"password\"]").type("admin123{enter}") // 輸入密碼后點擊Enter鍵登錄
cy.url().should("include", "/dashboard") // 通過獲取URL地址判斷登錄成功
cy.get("title").should("have.text", "儀表盤 - Halo") // 也可通過獲取網頁標題判斷登錄成功
cy.visit("/posts/list") // 進入文章列表
})
after("此處是后置操作,當前模塊下執行一次!",function (){
cy.log("****** 文章管理模塊用例執行完畢! ******")
})
it("查看文章列表", function () {
// 獲取文章列表字段,應有“標題狀態分類標簽評論訪問發布時間操作”,使用have.text時,文本內容必須一致,是相等關系
cy.get(".ant-table-column-title").should("have.text", "標題狀態分類標簽評論訪問發布時間操作")
});
it("寫文章並保存為草稿", function () {
cy.get("a > .ant-btn").click() // 點擊【+寫文章】按鈕
cy.get("[placeholder=\"請輸入文章標題\"]").type("寄黃幾復") // 定位並輸入文章標題
cy.get(".CodeMirror-line").type("桃李春風一杯酒,江湖夜雨十年燈。") // 定位並輸入文章內容
cy.get(".ant-space-item").children(".ant-btn-primary").click() // 點擊【發布】按鈕
cy.get(".ant-btn-danger").click().should("have.text", "保存成功") // 點擊【保存草稿】按鈕,應提示“保存成功”
cy.get(".no-underline").first().should("have.text", " 寄黃幾復 ") // 獲取文章列表應顯示新增的草稿文章
});
it("發布文章", function () {
cy.get("[data-row-key]>:nth-child(9)>:nth-child(5)").first().click() // 點擊【設置】
cy.get(".ant-modal-footer>:nth-child(3)").click().should("have.text", "保存成功") // 點擊【轉為發布】
cy.get(".ant-modal-footer>:nth-child(5)").click() // 關閉設置窗口
cy.get("[style=\"margin-right: 8px;\"]>.ant-btn").click() // 刷新頁面
cy.get(".ant-badge-status-text").first().should("have.text", "已發布") // 驗證文章狀態為“已發布”
});
it("文章移到回收站並刪除", function () {
cy.get("[data-row-key]>:nth-child(9)>:nth-child(3)").first().click() // 刪除第一條文章
cy.get(".ant-popover-buttons>.ant-btn-primary").as("OK").click() // 為元素設置別名,點擊確認刪除
// 通過獲取提示信息判斷刪除成功
cy.get(".ant-message-notice-content").as("Tips").should("have.text", "操作成功!")
cy.get(".mb-5>.ant-space>:nth-child(2)>.ant-btn").click() // 進入回收站
cy.get("[data-row-key]>:nth-child(7)>:nth-child(3)").first().click() // 刪除回收站第一條文章
cy.get("@OK").click() // 使用元素別名,確認刪除
cy.get("@Tips").should("have.text", "刪除成功!") // 使用元素別名,通過獲取提示信息判斷刪除成功
// 檢查回收站列表不應包含已刪除文章
cy.get(".ant-table-row-cell-ellipsis").should("not.contain.text", " 寄黃幾復 ")
cy.get(".ant-modal-footer>.ant-btn").click() // 關閉回收站窗口
})
})
// 下面的示例是結合上文before用法,介紹以下berfeEach的用法
describe("頁面管理",function (){
beforeEach("此處也是前置操作,與上文的before不同的,在每條用例前都會執行一次!",function (){
cy.log("~~~~~~ 開始執行新的用例!~~~~~~")
cy.visit("/login")
cy.get("[type=\"text\"]").type("admin")
cy.get("[type=\"password\"]").type("admin123{enter}")
})
afterEach("此處也是后操作,與上文的after不同的,在每條用例后都會執行一次!",function (){
cy.log("~~~~~~ 此用例執行完畢!~~~~~~")
})
it("查看獨立頁面",function (){
cy.visit("/sheets/list")
cy.get(".ant-table-column-title").should("have.text","頁面名稱訪問地址狀態操作")
cy.wait(4000).log("固定等待4s,否者報“訪問過於頻繁,請稍后再試!”")
});
it("查看新建頁面", function () {
cy.get("[aria-label=\"圖標: read\"]").click() // 點擊【頁面】主菜單
cy.contains("新建頁面").click() // 點擊【新建頁面】子菜單
cy.get(".ant-page-header-heading-title").should("have.text","新頁面")
});
})
運行結果如下圖所示

使用PO模型
通過上面示例可以看出,大量的定位元素和數據都耦合到整個測試步驟中,會增加后期維護難度,所以盡可能拆分出來,結合PO模型思想,將數據、定位、頁面和步驟進行拆分,實現解耦合,以登錄為例
-
先將定位分離出來,創建
locator.json
文件,使用json格式定義登錄的定位元素信息// locator.json { "login": { "username": "[type=\"text\"]", "passwd": "[type=\"password\"]", "submit": ".ant-btn" } }
-
然后定義頁面層,創建
login_page.js
文件,封裝頁面對象及業務流程// login_page.js import locator from "./data/locator.json" // 導入定位信息文件 export default class Login_page { // 導出class類 constructor() { // 使用構造方法定義URL this.url = "http://192.166.66.24:8090/admin/index.html#/login" } // 封裝頁面對象 visit(){ cy.visit(this.url) } get username(){ return cy.get(locator.login.username) } get passwd(){ return cy.get(locator.login.passwd) } get submit(){ return cy.get(locator.login.submit) } // 封裝登錄業務流 loginhalo(user,pwd){ if(user !== ""){ this.username.type(user) } if(pwd !== ""){ this.passwd.type(pwd) } this.submit.click() }
-
最后定義用例層,創建
login_case.js
文件,編寫測試用例// login_case.js describe("登錄測試",function (){ it("輸入正確的賬號密碼,登錄成功", function () { let login = new Login_page() // 定義一個對象 login.visit() // 打開URL login.loginhalo("admin","admin123") // 輸入賬號密碼 cy.url().should("include", "/dashboard") // 根據url判斷是否登錄成功 }); })
至此元素定位與測試步驟拆分完成,還可以繼續將步驟中的測試數據進行拆分,更方便進行參數化測試
-
繼續分離測試數據,並實現參數化,創建
login.json
文件,定義登錄信息及對應的斷言// login.json { "success": [{ "name": "輸入正確的賬號和密碼,應登錄成功", "username": "admin", "password": "admin123", "validate": { "checkpoint": ["url","include","/dashboard"]}}], "fail": [{ "name": "輸入錯誤的賬號和密碼,應提示“用戶名或者密碼不正確”", "username": "admin", "password": "123456", "validate": {"checkpoint": [".ant-message-custom-content>span","contain","用戶名或者密碼不正確"]}}, { "name": "輸入登錄密碼,賬號為空,應提示“用戶名不能為空”", "username": "", "password": "123456", "validate": {"checkpoint": [".ant-form-explain","contain","* 用戶名/郵箱不能為空"]}}, { "name": "輸入用戶名,密碼為空,應提示“密碼不能為空”", "username": "admin", "password": "", "validate": {"checkpoint": [".ant-form-explain","contain","* 密碼不能為空"]}}] }
-
修改測試用例
login_case.js
文件,代碼如下import data from "./data/login.json" // 導入登錄信息文件 import Login_page from "./login_page" // 導入login_page文件 describe("登錄功能驗證", function (){ beforeEach(function (){ // 配置前置條件 let loginHL = new Login_page() loginHL.visit() cy.wrap(loginHL).as("testlogin") // 返回傳遞給loginHL的對象,使用as命令設置別名,方便在測試用例中引用 }) afterEach(function (){ // 配置后置條件 cy.wait(4000) // 因測試平台限制不能短時間內連續登錄,故設置每次登錄間隔時間為4秒鍾 }) data.success.forEach(item => { // 遍歷login.json文件中success下的數據 it(item.name,function () { this.testlogin.loginhalo(item.username,item.password) //獲取賬號密碼后登錄 cy.url().should(item.validate.checkpoint[1],item.validate.checkpoint[2]) // 斷言結果 }) }) data.fail.forEach(item => { // 遍歷login.json文件中fail下的數據 it(item.name, function () { this.testlogin.loginhalo(item.username,item.password) cy.get(item.validate.checkpoint[0]).should(item.validate.checkpoint[1],item.validate.checkpoint[2]) }) }) })
至此實現數據、定位、頁面對象和測試用例實現分離,當定位信息和數據發生變化時,只需修改
locator.json
和login.json
兩個文件中的json數據,下圖是運行結果
命令運行測試用例
使用命令行運行會自動保存視頻,視頻保存在cypress/integration/videos/
目錄下,如果存在失敗的用例,則同時會保存失敗截圖,截圖保存cypress/integration/screenshots/
目錄下
npx cypress run // 運行integration目錄下所有用例
npx cypress run --browser chrome // 指定瀏覽器運行integration目錄下所有用例
npx cypress run --spec "cypress/integration/HL_login.js" // 運行指定的用例
生成測試報告
-
先安裝mochawesome相關模塊
npm install --save-dev mochawesome mochawesome-merge mochawesome-report-generator
-
在
cypress.json
文件中添加以下信息{ "reporter": "mochawesome", "reporterOptions": { "reportDir": "cypress/results", "overwrite": false, "html": false, "json": true } }
-
生成測試報告
npx cypress run --reporter mochawesome // 運行integration目錄下所有測試用例並生成報告所需數據 npx cypress run --reporter mochawesome --spec "cypress/integration/HL_login.js" // 運行指定用例並生成所需數據 npx mochawesome-merge "cypress/results/*.json" > mochawesome.json // 將生成的數據合並到一起並生成整合報告 // 最終報告HTML報告生成在mochawesome-report目錄下 cd cypress/results // 如果要生成指定用例的報告,可以執行"運行指定用例生成數據"的命令后,進入results目錄下 npx marge mochawesome001.json // 選擇剛剛生成的測試數據,生成報告
報告結果如下圖所示
也可以使用JUnit/Allure生成報告,具體看官網介紹吧!
API接口測試
語法
在Cypress中發起HTTP請求需使用cy.request()
,語法如下
cy.request(method,url,headers,body)
單接口
如下示例登錄接口測試
it("登錄接口", function () {
cy.request({ // 發起接口請求
method:"post", // 請求方式
url:"http://192.166.66.24:8090/api/admin/login", // 請求地址,url可使用baseUrl配置到cypress.json文件中
body:{"username": "admin","password": "admin123","authcode": null} // 請求體
}).then(response =>{ // 兩種斷言方式,一種是使用then獲取響應數據,然后進行斷言
expect(response.status).to.be.equal(200)
}).its("body").should("contain",{"status":200,"message":"OK"}) // 另一種斷言方式是使用its獲取響應結果進行斷言
})
接口關聯
在接口自動化中肯定會有參數關聯的情況,例如登錄成功獲取的token給后面的接口使用,在cypress中可以使用.as()、sessionStorage.setItem()或定義公共函數的方法保存數據給后面到的接口使用,
-
使用
.as()
方法,只能在同一個用例下使用,示例如下it("查看管理文章列表", function () { cy.request({ // 先登錄 method:"post", url:"/api/admin/login", body:{"username": "admin","password": "admin123","authcode": null} }) .its("body.data.access_token").as("token") // 登錄成功后獲取token值並設置別名“token” .then(function (){ cy.log(this.token) // 打印token,調試時多使用log cy.request({ // 查看文章管理列表 method:"get", url:"/api/admin/posts", headers:{"Content-Type": "application/json","Admin-Authorization":this.token} // 調用token }).its("body").should("contain",{"status":200,"message":"OK"}) }) })
-
使用
sessionStorage.setItem
設置token,其它接口用例都可以調用,更推薦此方式,有利於后面做接口自動化describe("接口測試",function (){ it('登錄成功,並提取token給其它的接口使用', function () { cy.request({ method:"post", url:"/api/admin/login", body:{"username": "admin","password": "admin123","authcode": null} }) .its("body.data.access_token").as("token") // 提取token值並設置別名為“token” .then(function (){ cy.wrap(sessionStorage.setItem("Token",this.token)) // 使用sessionStorage.setItem設置token }) }); it('查看文章管理列表', function () { const token = sessionStorage.getItem("Token") // 提取sessionStorage中的Token並賦值給token cy.request({ method:"get", url:"/api/admin/posts", headers: {"Content-Type": "application/json","Admin-Authorization":token} // 調用token }) }); })
-
定義公共函數,生成token,供其它接口調用
// Token.js 文件名 export default class { generateToken(){ cy.request({ method:"post", url:"http://192.166.66.24:8090/api/admin/login", body:{"username": "admin","password": "admin123","authcode": null} }).then(resp=>{ cy.wrap(resp.body.data.access_token).as("token") }) } }
編寫用例時導入定義的公共函數,就可以使用token啦,示例如下
import Token from "./Token" // 導入定義公共函數的文件 describe("文章管理->增刪改查操作", function () { before( function () { let token = new Token() // 測試前先獲取token token.generateToken() }) it("查看文章管理列表", function () { cy.request({ method: "get", url: "/api/admin/posts", headers: {"Admin-Authorization": this.token} }).its("body").should("contain",{"status":200,"message":"OK"}) }); it("發布文章", function () { cy.request({ method:"post", url:"/api/admin/posts", headers: {"Content-Type": "application/json", "Admin-Authorization": this.token}, //調用token body:{"title":"test321","content":"<p>皮之不存,毛將焉附。</p>","status":"PUBLISHED"} }).its("body.data.id").as("articleID").then(function (){ cy.wrap(sessionStorage.setItem("ID",this.articleID)) }) }); it("將文章放到回收站", function () { let artId = sessionStorage.getItem("ID") cy.request({ method:"put", url:"/api/admin/posts/"+artId+"/status/RECYCLE", headers: {"Content-Type": "application/json", "Admin-Authorization": this.token}, //調用token }).its("body").should("contain",{"status":200,"message":"OK"}) }); it("從回收站刪除", function () { let deleteArtId = sessionStorage.getItem("ID") cy.request({ method:"delete", url:"/api/admin/posts", headers: {"Content-Type": "application/json", "Admin-Authorization": this.token}, //調用token body:[deleteArtId] }).its("body").should("contain",{"status":200,"message":"OK"}) }); })
接口參數化
-
使用數組做參數化,創建
param_API.js
文件// param_API.js import Token from "./Token" // 導入Token.json describe("查看列表並發布文章",function () { before(function () { // 前置條件,先獲取token let token = new Token() token.generateToken() }) let testdatas = [ // 測試數據 { "casename": "查看文章管理列表", "url": "/api/admin/posts", "method": "get", "headers": {"Content-Type": "application/json"}, "body": "", "status": 200, "message":"OK" }, { "casename": "發布文章", "url": "/api/admin/posts", "method":"post", "headers": {"Content-Type": "application/json"}, "body":{"title":"yadian","content":"<p>皮之不存,毛將焉附。</p>","status":"PUBLISHED"}, "status": 200, "message":"OK" } ] for (const data in testdatas) { // 遍歷測試數據進行測試 it(`${testdatas[data].casename}`, function () { let url = testdatas[data].url let method = testdatas[data].method let header = testdatas[data].headers let body = testdatas[data].body let status = testdatas[data].status let message = testdatas[data].message cy.request({url: url, method: method, headers: {header,"Admin-Authorization": this.token}, body: body}).then(function (resp) { // 斷言,判斷狀態碼和響應信息是否正確 expect(resp.status).to.eq(status) expect(resp.body.message).to.eq(message) }) }); } })
-
使用JSON文件做參數化
也可以將數據單獨分離出來,使用json文件做參數化,創建
testdata.json
文件,保存測試數據,如下// testdata.json [ { "casename": "查看文章管理列表", "url": "/api/admin/posts", "method": "get", "headers": {"Content-Type": "application/json"}, "body": "", "status": 200, "message":"OK" }, { "casename": "發布文章", "url": "/api/admin/posts", "method":"post", "headers": {"Content-Type": "application/json"}, "body":{"title":"yadian","content":"<p>皮之不存,毛將焉附。</p>","status":"PUBLISHED"}, "status": 200, "message":"OK" } ]
在用例腳本導入數據即可使用,修改
param_API.js
文件// param_API.js import Token from "./Token" // 導入Token.json import testdatas from "./testdata.json" // 導入數據文件testdata.json describe("查看列表並發布文章",function () { before(function () { // 前置條件,先獲取token let token = new Token() token.generateToken() }) for (const data in testdatas) { // 遍歷測試數據進行測試 it(`${testdatas[data].casename}`, function () { let url = testdatas[data].url let method = testdatas[data].method let header = testdatas[data].headers let body = testdatas[data].body let status = testdatas[data].status let message = testdatas[data].message cy.request({url: url, method: method, headers: {header,"Admin-Authorization": this.token}, body: body}).then(function (resp) { // 斷言,判斷狀態碼和響應信息是否正確 expect(resp.status).to.eq(status) expect(resp.body.message).to.eq(message) }) }); } })
其它
對於Web頁面測試,Cypress是支持錄制功能的,但是不推薦使用,一些可變元素可能會出現在錄制腳本中導致回放失敗,寫此文章時該功能處於試驗階段,因此默認是隱藏的,需要自行開啟,不排除后續平台放棄此功能,開啟方法:在cypress.json
文件中添加以下信息
{"experimentalStudio": true}
開啟后頁面就會出現錄制入口啦!如下圖演示:

對於非Cypress造成的報錯,報uncaught:exception
,此時用例無法完成,可以先忽略應用程序的報錯。忽略方法:打開support目錄下的index.js
文件,添加以下忽略命令
// 忽略所有uncaught:exception異常
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
// 若不想忽略所有異常,可忽略指定條件的異常,添加以下信息
Cypress.on('uncaught:exception', (err, runnable) => {
if (err.message.includes('HaloRestAPIError')) { // 指定的異常報錯,比如HaloRestAPIError
return false
}
})

這個Cypress官方文檔還是蠻詳細的,其它功能請自行探索吧!