本文章基于codeceptjs@1.0.1
codeceptjs介紹
codeceptjs是Codeception測試框架下的一個項目,是運行在nodejs的UI測試框架, 能使場景驅動的驗收測試簡單化指巡。
"Modern Era Acceptance Testing Framework for NodeJS"是codeceptjs官網(wǎng)最顯著的標語,那作為一個UI測試框架稳摄,究竟有哪些特點:
- 從用戶角度編寫。codeceptjs提供的API具有declarative testing的特點饲宿,從用戶行為的角度抽象出了許多與瀏覽器的交互動作厦酬,測試代碼非常易讀易懂。codeceptjs提供的API由于其易讀性也可以當成DSL來使用瘫想,這些DSL隱藏了后端webdriver服務的復雜性弃锐,可以讓使用者更加專注到測試場景的編寫中
-
所有的交互動作來自于對象
I
。對象I
的方法都被描述為用戶訪問網(wǎng)站可能產生的行為殿托,易讀易寫易維護霹菊,對非技術人員也很友好 -
對多個后端API兼容。codeceptjs支持多個webdriver實現(xiàn) i.e. webdriverio, protractor and phantomjs, 我們可以很簡單在他們之間切換。這些不同的webdriver實現(xiàn)在codeceptjs里面叫做Helper旋廷,正是下面這些Helper給對象
I
提供了actions - 同步鸠按。我們不用關心js中的異步問題 i.e. promises & callback。測試場景是線性的
- 基于Mocha測試框架饶碘。
- 可以編寫出易讀易懂的場景驅動的驗收測試目尖。可以寫成BDD style
- Smart locator扎运。Smart locators: use names, labels, matching text, CSS or XPath to locate elements
- 互動式debug shell瑟曲。可以在測試執(zhí)行時隨時暫停然后嘗試codeceptjs提供地不同的API
- 提供了方便的命令行工具幫助創(chuàng)建測試豪治《床Γ可以通過命令一步創(chuàng)建tests, pageobjects, stepobjects
- 支持web和mobile端的測試
說了這么多特點,它長什么樣子呢负拟?一個簡單的demo如下:
Feature('CodeceptJS demo');
Scenario('check Welcome page on site', (I) => {
I.amOnPage('/');
I.see('Welcome');
});
是不是可以基本當plain text來讀烦衣?:)
codeceptjs安裝
環(huán)境檢查
在安裝codeceptjs之前,你要確保你的node版本是大于6.11的
$ node -v
v8.3.0
安裝codeceptjs
使用npm全局安裝codeceptjs掩浙,這樣的話我們就可以在shell里面直接使用codeceptjs
命令
[sudo] npm install -g codeceptjs
本地安裝, 這樣的話我們可以執(zhí)行./node_modules/.bin/codeceptjs
npm install --save-dev codeceptjs
安裝browser driver
全局安裝webdriverio
[sudo] npm install -g webdriverio
本地安裝webdriverio
npm install webdriverio --save-dev
全局安裝protractor
[sudo] npm install -g protractor
本地安裝protractor
npm install protractor --save-dev
全局安裝nightmare
[sudo] npm install -g nightmare nightmare-upload
本地安裝nightmare
npm install nightmare nightmare-upload --save-dev
全局安裝codeceptjs+webdriverio
[sudo] npm install -g codeceptjs-webdriverio
本地安裝codeceptjs+webdriverio
[sudo] npm install codeceptjs-webdriverio --save-dev
全局安裝codeceptjs+nightmare
[sudo] npm install -g codeceptjs-nightmare
本地安裝codeceptjs+nightmare
[sudo] npm install codeceptjs-nightmare --save-dev
全局安裝codeceptjs+protractor
[sudo] npm install -g codeceptjs-protractor
本地安裝codeceptjs+protractor
[sudo] npm install codeceptjs-protractor --save-dev
安裝完之后你可以通過codeceptjs
命令查看codeceptjs的版本和常用命令參數(shù)介紹
? codeceptjs-demo git:(master) ? codeceptjs
CodeceptJS v1.0.1
Usage: [options] [command]
Options:
-h, --help output usage information
Commands:
init [path] Creates dummy config in current dir or [path]
shell|sh [options] [path] Interactive shell
list|l [path] List all actions for I.
def [path] List all actions for I.
generate:test|gt [path] Generates an empty test
generate:pageobject|gpo [path] Generates an empty page object
generate:object|go [options] [path] Generates an empty support object (page/step/fragment)
generate:helper|gh [path] Generates a new helper
run [options] [test] Executes tests
run-multiple [options] [suites...] Executes tests multiple
其中參數(shù)list
可以列出對象I
所有的動作花吟,也就是我們可以利用的瀏覽器交互API
? codeceptjs-demo git:(master) ? codeceptjs list
List of test actions: --
WebDriverIO I.defineTimeout(timeouts)
WebDriverIO I.amOnPage(amOnPage)
WebDriverIO I.click(click)
WebDriverIO I.doubleClick(doubleClick)
WebDriverIO I.rightClick(rightClick)
..........
安裝selenium-standalone
因為這些helper i.e. webdriverio
, protractor
都是需要selenium作為后端與browser通信的服務,所以我們也需要安裝selenium厨姚。
下面的命令會安裝selenium standalone server以及相應的browser driver
npm install selenium-standalone@latest -g
selenium-standalone install
selenium-standalone start
詳細的selenium-standalone資料請參考這里
codeceptjs基礎知識
命令行使用
codeceptjs init
可以用在當我們需要初始化一個測試項目的時候衅澈。這個命令會創(chuàng)建和配置一些文件來搭建我們的測試環(huán)境。
? codeceptjs-init: codeceptjs init
Welcome to CodeceptJS initialization tool
It will prepare and configure a test environment for you
Installing to /Users/diyu/workspace/codeceptjs_demo/codeceptjs-init
? Where are your tests located? ./*_test.js
? What helpers do you want to use? WebDriverIO
? Where should logs, screenshots, and reports to be stored? ./output
? Would you like to extend I object with custom steps? Yes
? Do you want to choose localization for tests? English (no localization)
? Where would you like to place custom steps? ./steps_file.js
Configure helpers...
? [WebDriverIO] Base url of site to be tested http://localhost
? [WebDriverIO] Browser in which testing will be performed chrome
Steps file created at /Users/diyu/workspace/codeceptjs_demo/codeceptjs-init/steps_file.js
Config created at /Users/diyu/workspace/codeceptjs_demo/codeceptjs-init/codecept.json
Directory for temporary output files created at `_output`
Almost done! Create your first test by executing `codeceptjs gt` (generate test) command
? codeceptjs-init: ll
total 16
-rw-r--r-- 1 diyu staff 291B 8 27 22:39 codecept.json
drwxr-xr-x 2 diyu staff 68B 8 27 22:39 output
-rw-r--r-- 1 diyu staff 281B 8 27 22:39 steps_file.js
如上所示谬墙,一些測試文件今布、配置文件和輸出目錄都創(chuàng)建好了。具體到這些文件是干嘛的芭梯,里面內容是什么险耀,之后的學習筆記后提到弄喘。
codeceptjs gt
會創(chuàng)建測試文件
? codeceptjs-init: codeceptjs gt
Creating a new test...
----------------------
? Filename of a test first
? Feature which is being tested first demo
Test for first was created in /Users/diyu/workspace/codeceptjs_demo/codeceptjs-init/first_test.js
? codeceptjs-init: ll
total 24
-rw-r--r-- 1 diyu staff 291B 8 27 22:39 codecept.json
-rw-r--r-- 1 diyu staff 66B 8 27 23:50 first_test.js
drwxr-xr-x 2 diyu staff 68B 8 27 22:39 output
-rw-r--r-- 1 diyu staff 281B 8 27 22:39 steps_file.js
? codeceptjs-init: cat first_test.js
Feature('first demo');
Scenario('test something', (I) => {
});
first_test.js 就是被創(chuàng)建的測試文件
codeceptjs run
會運行測試玖喘。加上參數(shù)--steps
會輸出詳細的執(zhí)行過程
? codeceptjs-init: codeceptjs run --steps
CodeceptJS v1.0.1
Using test root "/Users/diyu/workspace/codeceptjs_demo/codeceptjs-init"
first demo --
test something
? OK in 1ms
OK | 1 passed // 2s
codeceptjs gpo
會創(chuàng)建一個page object file。這個命令可以快速簡單的幫助你實現(xiàn)page object設計模式
? codeceptjs-init codeceptjs gpo
Creating a new page object
--------------------------
? Name of a page object landingPage
? Where should it be stored ./pages/landingPage.js
Updating configuration file...
Page object for landingPage was created in /Users/diyu/workspace/codeceptjs_demo/codeceptjs-init/pages/landingPage.js
Use landingPagePage as parameter in test scenarios to access it
? codeceptjs-init ll
total 24
-rw-r--r-- 1 diyu staff 375B 8 29 21:30 codecept.json
-rw-r--r-- 1 diyu staff 66B 8 27 23:50 first_test.js
drwxr-xr-x 2 diyu staff 68B 8 27 22:39 output
drwxr-xr-x 3 diyu staff 102B 8 29 21:30 pages
-rw-r--r-- 1 diyu staff 281B 8 27 22:39 steps_file.js
landingPage.js被創(chuàng)建在pages目錄下面蘑志,這個文件里面會有l(wèi)andingPage的元素定位和基本操作累奈。一個簡單的demo如下
'use strict';
let I;
module.exports = {
_init() {
I = actor();
},
fields: {
email: '#user_basic_email',
password: '#user_basic_password'
},
submitButton: {css: '#new_user_basic input[type=submit]'},
sendForm(email, password) {
I.fillField(this.fields.email, email);
I.fillField(this.fields.password, password);
I.click(this.submitButton);
}
}
通過參數(shù)傳遞的方式,我們可以在測試文件里面注入頁面元素和操作
Feature('CodeceptJS Demonstration');
Before((I) => { // or Background
I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
});
Scenario('test some forms', (I, docsPage) => {
docsPage.sendForm('hello@world.com','123456');
I.see('User is valid');
I.dontSeeInCurrentUrl('/documentation');
});
這樣的話急但,最簡單的page object就可以用了
Actions and Assertions
下面通過一個簡單的demo來看下codeceptjs測試文件長什么樣子
Feature('CodeceptJS Demonstration');
Scenario('test some forms', (I) => {
I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
I.fillField('Email', 'hello@world.com');
I.fillField('Password', '123456');
I.checkOption('Active');
I.checkOption('Male');
I.click('Create User');
I.see('User is valid');
I.dontSeeInCurrentUrl('/documentation');
});
可以看出
- 所有的頁面操作都由對象
I
調用執(zhí)行澎媒,而I
的方法都來自于Helper - see(),dontSee()方法是用來做驗證的波桩〗渑可以在頁面上找到指定元素進行驗證.如果第二個參數(shù)提供,可以縮小元素查找范圍镐躲。
I.see('User is valid', '.alert-success');
- Feature和Scenario來源于mocha
- fillField(), checkOption()和click()這些方法工作原理類似储玫,都是通過name, css和xpath找到元素并與之交互
Grabber
當我們需要從頁面中抓取元素然后用到后續(xù)測試的時候侍筛,我們可以使用帶grab
前綴的方法。它的用法如下
var assert = require('assert');
Feature('CodeceptJS Demonstration');
Scenario('test page title', function*(I) {
I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
var title = yield I.grabTitle();
assert.equal(title, 'Example application with SimpleForm and Twitter Bootstrap');
});
grad()要被使用在generator里面撒穷,所以帶有yield關鍵字
debug
codeceptjs可以通過兩種方式進行測試代碼開發(fā)中的調試
- 在測試文件中使用
pause()
方法
? codeceptjs-init codeceptjs run --steps
CodeceptJS v1.0.1
Using test root "/Users/diyu/workspace/codeceptjs_demo/codeceptjs-init"
CodeceptJS Demonstration --
test some forms
? I am on page "http://simple-form-bootstrap.plataformatec.com.br/documentation"
? I fill field "Email", "hello@world.com"
? I fill field "Password", "123456"
? I check option "Active"
Interative debug session started
Use JavaScript syntax to try steps in action
Press ENTER to continue
I.
Exiting interactive shell....
? I check option "Male"
? I click "Create User"
? I see "User is valid"
? I dont see in current url "/documentation"
? OK in 12581ms
注意到中間突然出現(xiàn)了交互式的debug session匣椰,這就是pause()的作用
- 下面這種
codeceptjs shell
來進行調試
? codeceptjs-init codeceptjs shell
String interactive shell for current suite...
Interative debug session started
Use JavaScript syntax to try steps in action
Press ENTER to continue
I.amOnPage('www.hupu.com')
I.
通過這個互動的debug session,我們可以一步步的輸入不同的action來觀察瀏覽器的行為
Before()/After()
codeceptjs也提供了測試框架必不可少的用作測試準備setup和清理teardown的方法端礼。Before()和Background()可以用于抽取公共方法或做測試準備工作禽笑,After()可以用于測試收尾清理工作。
Before()和Background()會在所有Scenario()之前執(zhí)行一樣蛤奥,After()會在所有Scenario()之后執(zhí)行佳镜。
下面的測試代碼片段中Before()會在所有Scenario()方法之前執(zhí)行。Before()也可以換成Background()喻括。
Feature('CodeceptJS Demonstration');
Before((I) => { // or Background
I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
});
After((I) => {
I.clearCookie();
});
Scenario('test some forms', (I) => {
I.click('Create User');
I.see('User is valid');
I.dontSeeInCurrentUrl('/documentation');
});
Scenario('test title', (I) => {
I.seeInTitle('Example application');
});
BeforeSuite()/AfterSuite()
當你需要在所有的測試Scenario()之前進行復雜的setup和teardown邀杏,BeforeSuite()/AfterSuite()會是好的選擇。BeforeSuite()會在所有的Scenario(), Before()之前調用運行唬血;同理望蜡,AfterSuite()會在所有的Scenario(), Before()之后調用運行;這兩個方法有且只運行一次拷恨。
由于BeforeSuite()/AfterSuite()有可能是在瀏覽器打開之前運行脖律,所有BeforeSuite()/AfterSuite()只能訪問到I
對象。BeforeSuite()/AfterSuite()只會在聲明的文件中生效腕侄,所以不同的文件就可以有不同的BeforeSuite()/AfterSuite()了小泉。
BeforeSuite((I) => {
I.syncDown('testfolder');
});
AfterSuite((I) => {
I.syncUp('testfolder');
I.clearDir('testfolder');
});
within('section', ()=>{})
使用within('section', ()=>{})可以讓一系列I
對象的方法在頁面某個特定區(qū)域執(zhí)行.這樣可以鎖定具體的執(zhí)行范圍。
Feature('within demo')
Scenario('login github', (I)=>{
I.amOnPage('https://github.com');
within('.form-signup-home', () => {
I.fillField('user[login]', 'User');
I.fillField('user[email]', 'user@user.com');
I.fillField('user[password]', 'user@user.com');
I.click('button');
});
I.see('There were problems creating your account.');
});
Comments I.say()
給測試場景添加注釋冕杠,可以使用I.say()
微姊。
Feature('within demo')
Scenario('login github', (I)=>{
I.amOnPage('http://www.baidu.com');
I.say('Above is the link for baidu search engine');
});
Skip test
使用xScenario()可以跳過測試的執(zhí)行。
只執(zhí)行一個測試場景
使用Scenario.only()可以只運行該測試分预。
Reporter
之前提到codeceptjs是基于mocha的兢交,所有mocha的--reporter也對codeceptjs有效。我們可以用下面命令指定report的格式
codeceptjs run --steps --reporter spec (這是default的report格式)
spec
可以替換為dot
, nyan
, tap
等笼痹。詳細內容請參考這里配喳。
XML report
使用mocha-junit-reporter可以產生一個xml報告,這對jenkins非常友好凳干。
安裝 npm i mocha-junit-reporter
把報告放到output目錄下
"mocha": {
"reporterOptions": {
"mochaFile": "output/result.xml"
}
},
使用codeceptjs run --reporter mocha-junit-reporter
來執(zhí)行測試產生xml報告晴裹,最終的報告會被放在 output/result.xml。
HTML report
因為codeceptjs是基于mocha的救赐,所以我們可以使用mochawesome來產生好看的HTML報告涧团。
安裝 npm i mochawesome
把報告放到output目錄下
"mocha": {
"reporterOptions": {
"reportDir": "output"
}
},
使用codeceptjs run --reporter mochawesome
來執(zhí)行測試產生html報告,最終的報告會被放在 output/mochawesome.html。
對于失敗的測試泌绣,我們需要有截圖喳瓣,我們可以使用下面的配置來實現(xiàn)
"helpers": {
"Mochawesome": {
"uniqueScreenshotNames": "true"
}
},
Test Options
Feature()和Scenario()的第二個參數(shù)可以是一對鍵值對,這個參數(shù)的作用是為測試場景提供一些選項比如timeout和retry
Feature('My feature', {key: val});
Scenario('My scenario', {key: val}, (I) => {});
Timeout
默認狀態(tài)下赞别,測試場景是沒有timeout的畏陕,我們可以通過傳遞下面的鍵值對來設置feature級別或是scenario級別的timeout
// set timeout to 5s
Feature('Stop me', {timeout: 5000});
// set timeout to 1s
Scenario("Stop me faster", {timeout: 1000}, (I) => {});
// disable timeout for this scenario
Scenario("Don't stop me", {timeout: 0}, (I) => {});
Retries
UI測試是不穩(wěn)定的,有些時候我們需要rerun來讓相應的測試場景通過仿滔。我們可以通過在Feature()和Scenario()中傳遞retries鍵值對來實現(xiàn)惠毁。
//所有scenario失敗后都會retry3次
Feature('Complex JS Stuff', {retries: 3})
//這個scenario失敗后只會retry1次
Scenario('Not that complex', {retries: 1}, (I) => {
// test goes here
});
//這個scenario失敗后會retry3次
Scenario('Really complex', (I) => {
// test goes here
});