e2e測試(End-to-End testing)是模擬使用者的行為,實際打開瀏覽器操作頁面的測試方式,因此不限於vue, react的專案,e2e的測試可以反映出頁面在不同瀏覽器的執行情形。
Cypress便是其中一個常見工具,Cypress在安裝上簡單,語法採用 mocha 的句法、chai 的斷詞(expect, should, assert),撰寫的方式跟 unit test 相似。
安裝
進到想要測試的專案目錄內,使用 npm 安裝。(官網說明)
接著在目錄裡,建立先建立一個 cypress.json,內容放一個空物件,此時你可以直接執行下面的命令(官網說明):
會發現多了一個 cypress/ 的資料夾,裡面有四個目錄:
- integration: 主要的測試檔案
- fixtures: 固定的資料
- plugins: 主要有 on 定義全局的不同時間點的執行工作,以及 config 提供全局使用的參數,如常用的 env
- support: 可提供自定義的指令、或引入擴充的指令,供撰寫測試檔案使用
Cypress.json 與環境變數(.env)
使用 cypress 都必須在專案的根目錄裡新增 cypress.json,如果直接放 {} 空物件,代表直接使用預設值。
下面是我的設定,從字面上不難理解設定的內容,第一個testFiles是測試的檔案名稱,接著各個目錄的路徑,video是 Boolean 決定是否錄製測試過程的影片,screenshotOnRunFailure也是 Boolean 是否在執行失敗時擷取畫面。
{ "testFiles": "**/*.spec.js", "integrationFolder": "resources/cypress/integration", "componentFolder": "resources/cypress/components", "fixturesFolder": "resources/cypress/fixtures", "pluginsFile": "resources/cypress/plugins", "supportFile": "resources/cypress/support", "video": false, "screenshotOnRunFailure": false }
除了cypress基本的設定,常常也需要讀取環境設定檔(.env),假設內容如下(官方說明):
APP_URL=https://chenuin.github.io/ USER_NAME=chenuin
在plugins/index.js可以設定:
// plugins/index.js require('dotenv').config(); module.exports = (on, config) => { config.baseUrl = process.env.APP_URL; config.env.USER_NAME = process.env.USER_NAME; return config; }測試檔案需要使用時,可以直接呼叫:
Cypress.config('baseUrl')
Cypress.env('USER_NAME')
打開GUI,點選上方的 Settings 可以看到設定的結果,上面的顏色可以區分目前的設定是讀取哪裡,像是 baseUrl 是紫色的 plugin,
因此,可以知道下面兩種寫法都可以達到相同的效果:
// plugins/index.js config.baseUrl = process.env.APP_URL; // cypress.json { "baseUrl": "https://chenuin.github.io/" }
執行
提供 GUI 介面操作
無GUI,在 Terminal 上執行
訪問頁面 visit
測試的第一步常常是先進入某一個頁面,通常專案內都在相同的Domain下,如果有設定 baseUrl,Cypress會直接將設定作為前綴,導到相應的頁面,在寫法上簡潔許多。
// https://chenuin.github.io/webpack/2021/08/08/webpack5-getting-started.html cy.visit('/webpack/2021/08/08/webpack5-getting-started.html')
若未設定,需要傳入完整的路徑,或者cypress會嘗試在你的web server上找到對應位置的頁面。(官方說明)
intercept與fixtures的使用
使用 intercept 來監聽由頁面上請求(request)及回應(response),下面三者是相同的意思:
cy.intercept('/users/**') cy.intercept('GET', '/users/**') cy.intercept({ method: 'GET', url: '/users/**', })
你也可以為這個路由(route)新增別名(alias),除了方便辨識外,如果需要在request後執行的工作,可以使用下面的寫法:
cy.intercept('POST', '/users').as('createUser'); // once a request to create user responds, 'cy.wait' will resolve cy.wait('@createUser')
GUI上可以看到這個 route 表格
執行此請求時會留下Log記錄,所有被監聽的路由會列在route表格,表格上可以看到所有route被呼叫的次數(表格最右側的數字),對應的別名(alias)是什麼。
上面 Stubbed 是代表回應(response)是否被取代了,這個是很好用的功能,例如:當我們將 users 這個請求的回應,取代為一個列表:
// requests to '/users' will be fulfilled // with a body of list: ['John', 'Ben', 'Mary] cy.intercept( '/users', ['John', 'Ben', 'Mary], )
或者可以使用 fixture ,後面填寫 fixture/ 目錄裡的檔案名稱。
// requests to '/users' will be fulfilled // with the contents of the "users.json" fixture cy.intercept( '/users', { fixture: 'users.json' }, )(官方說明)
ESLint設定
請在 .eslintrc.js 加上設定,不需要安裝任何套件:
module.exports = { extends: [ 'plugin:cypress/recommended', ], };