codeceptjs學(xué)習(xí)筆記 - 安裝和基本知識(shí)

本文章基于codeceptjs@1.0.1

codeceptjs介紹

codeceptjsCodeception測(cè)試框架下的一個(gè)項(xiàng)目,是運(yùn)行在nodejs的UI測(cè)試框架, 能使場(chǎng)景驅(qū)動(dòng)的驗(yàn)收測(cè)試簡(jiǎn)單化。

"Modern Era Acceptance Testing Framework for NodeJS"是codeceptjs官網(wǎng)最顯著的標(biāo)語(yǔ),那作為一個(gè)UI測(cè)試框架,究竟有哪些特點(diǎn):

  • 從用戶角度編寫(xiě)。codeceptjs提供的API具有declarative testing的特點(diǎn),從用戶行為的角度抽象出了許多與瀏覽器的交互動(dòng)作,測(cè)試代碼非常易讀易懂。codeceptjs提供的API由于其易讀性也可以當(dāng)成DSL來(lái)使用,這些DSL隱藏了后端webdriver服務(wù)的復(fù)雜性,可以讓使用者更加專注到測(cè)試場(chǎng)景的編寫(xiě)中
  • 所有的交互動(dòng)作來(lái)自于對(duì)象I。對(duì)象I的方法都被描述為用戶訪問(wèn)網(wǎng)站可能產(chǎn)生的行為,易讀易寫(xiě)易維護(hù),對(duì)非技術(shù)人員也很友好
  • 對(duì)多個(gè)后端API兼容。codeceptjs支持多個(gè)webdriver實(shí)現(xiàn) i.e. webdriverio, protractor and phantomjs, 我們可以很簡(jiǎn)單在他們之間切換。這些不同的webdriver實(shí)現(xiàn)在codeceptjs里面叫做Helper,正是下面這些Helper給對(duì)象I提供了actions
  • 同步。我們不用關(guān)心js中的異步問(wèn)題 i.e. promises & callback。測(cè)試場(chǎng)景是線性的
  • 基于Mocha測(cè)試框架
  • 可以編寫(xiě)出易讀易懂的場(chǎng)景驅(qū)動(dòng)的驗(yàn)收測(cè)試。可以寫(xiě)成BDD style
  • Smart locator。Smart locators: use names, labels, matching text, CSS or XPath to locate elements
  • 互動(dòng)式debug shell。可以在測(cè)試執(zhí)行時(shí)隨時(shí)暫停然后嘗試codeceptjs提供地不同的API
  • 提供了方便的命令行工具幫助創(chuàng)建測(cè)試。可以通過(guò)命令一步創(chuàng)建tests, pageobjects, stepobjects
  • 支持web和mobile端的測(cè)試

說(shuō)了這么多特點(diǎn),它長(zhǎng)什么樣子呢?一個(gè)簡(jiǎn)單的demo如下:

Feature('CodeceptJS demo');

Scenario('check Welcome page on site', (I) => {
  I.amOnPage('/');
  I.see('Welcome');
});

是不是可以基本當(dāng)plain text來(lái)讀?:)

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

安裝完之后你可以通過(guò)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可以列出對(duì)象I所有的動(dòng)作,也就是我們可以利用的瀏覽器交互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

因?yàn)檫@些helper i.e. webdriverio, protractor都是需要selenium作為后端與browser通信的服務(wù),所以我們也需要安裝selenium。

下面的命令會(huì)安裝selenium standalone server以及相應(yīng)的browser driver

npm install selenium-standalone@latest -g
selenium-standalone install
selenium-standalone start

詳細(xì)的selenium-standalone資料請(qǐng)參考這里

codeceptjs基礎(chǔ)知識(shí)

命令行使用

codeceptjs init可以用在當(dāng)我們需要初始化一個(gè)測(cè)試項(xiàng)目的時(shí)候。這個(gè)命令會(huì)創(chuàng)建和配置一些文件來(lái)搭建我們的測(cè)試環(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

如上所示,一些測(cè)試文件、配置文件和輸出目錄都創(chuàng)建好了。具體到這些文件是干嘛的,里面內(nèi)容是什么,之后的學(xué)習(xí)筆記后提到。

codeceptjs gt會(huì)創(chuàng)建測(cè)試文件
?  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)建的測(cè)試文件

codeceptjs run 會(huì)運(yùn)行測(cè)試。加上參數(shù)--steps會(huì)輸出詳細(xì)的執(zhí)行過(guò)程
?  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會(huì)創(chuàng)建一個(gè)page object file。這個(gè)命令可以快速簡(jiǎn)單的幫助你實(shí)現(xiàn)page object設(shè)計(jì)模式
?  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目錄下面,這個(gè)文件里面會(huì)有l(wèi)andingPage的元素定位和基本操作。一個(gè)簡(jiǎn)單的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);
  }
}

通過(guò)參數(shù)傳遞的方式,我們可以在測(cè)試文件里面注入頁(yè)面元素和操作

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');
});

這樣的話,最簡(jiǎn)單的page object就可以用了

Actions and Assertions

下面通過(guò)一個(gè)簡(jiǎn)單的demo來(lái)看下codeceptjs測(cè)試文件長(zhǎng)什么樣子

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');
});

可以看出

  • 所有的頁(yè)面操作都由對(duì)象I調(diào)用執(zhí)行,而I的方法都來(lái)自于Helper
  • see(),dontSee()方法是用來(lái)做驗(yàn)證的。可以在頁(yè)面上找到指定元素進(jìn)行驗(yàn)證.如果第二個(gè)參數(shù)提供,可以縮小元素查找范圍。I.see('User is valid', '.alert-success');
  • Feature和Scenario來(lái)源于mocha
  • fillField(), checkOption()和click()這些方法工作原理類似,都是通過(guò)name, css和xpath找到元素并與之交互

Grabber

當(dāng)我們需要從頁(yè)面中抓取元素然后用到后續(xù)測(cè)試的時(shí)候,我們可以使用帶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關(guān)鍵字

debug

codeceptjs可以通過(guò)兩種方式進(jìn)行測(cè)試代碼開(kāi)發(fā)中的調(diào)試

  • 在測(cè)試文件中使用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來(lái)進(jìn)行調(diào)試
?  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.

通過(guò)這個(gè)互動(dòng)的debug session,我們可以一步步的輸入不同的action來(lái)觀察瀏覽器的行為

Before()/After()

codeceptjs也提供了測(cè)試框架必不可少的用作測(cè)試準(zhǔn)備setup和清理teardown的方法。Before()和Background()可以用于抽取公共方法或做測(cè)試準(zhǔn)備工作,After()可以用于測(cè)試收尾清理工作。

Before()和Background()會(huì)在所有Scenario()之前執(zhí)行一樣,After()會(huì)在所有Scenario()之后執(zhí)行。

下面的測(cè)試代碼片段中Before()會(huì)在所有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()

當(dāng)你需要在所有的測(cè)試Scenario()之前進(jìn)行復(fù)雜的setup和teardown,BeforeSuite()/AfterSuite()會(huì)是好的選擇。BeforeSuite()會(huì)在所有的Scenario(), Before()之前調(diào)用運(yùn)行;同理,AfterSuite()會(huì)在所有的Scenario(), Before()之后調(diào)用運(yùn)行;這兩個(gè)方法有且只運(yùn)行一次。

由于BeforeSuite()/AfterSuite()有可能是在瀏覽器打開(kāi)之前運(yùn)行,所有BeforeSuite()/AfterSuite()只能訪問(wèn)到I對(duì)象。BeforeSuite()/AfterSuite()只會(huì)在聲明的文件中生效,所以不同的文件就可以有不同的BeforeSuite()/AfterSuite()了。

BeforeSuite((I) => {
  I.syncDown('testfolder');
});

AfterSuite((I) => {
  I.syncUp('testfolder');
  I.clearDir('testfolder');
});

within('section', ()=>{})

使用within('section', ()=>{})可以讓一系列I對(duì)象的方法在頁(yè)面某個(gè)特定區(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()

給測(cè)試場(chǎng)景添加注釋,可以使用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()可以跳過(guò)測(cè)試的執(zhí)行。

只執(zhí)行一個(gè)測(cè)試場(chǎng)景

使用Scenario.only()可以只運(yùn)行該測(cè)試。

Reporter

之前提到codeceptjs是基于mocha的,所有mocha的--reporter也對(duì)codeceptjs有效。我們可以用下面命令指定report的格式

codeceptjs run --steps --reporter spec (這是default的report格式)

spec可以替換為dot, nyan, tap等。詳細(xì)內(nèi)容請(qǐng)參考這里

XML report

使用mocha-junit-reporter可以產(chǎn)生一個(gè)xml報(bào)告,這對(duì)jenkins非常友好。

安裝 npm i mocha-junit-reporter

把報(bào)告放到output目錄下

"mocha": {
  "reporterOptions": {
      "mochaFile": "output/result.xml"
  }
},

使用codeceptjs run --reporter mocha-junit-reporter 來(lái)執(zhí)行測(cè)試產(chǎn)生xml報(bào)告,最終的報(bào)告會(huì)被放在 output/result.xml。

HTML report

因?yàn)閏odeceptjs是基于mocha的,所以我們可以使用mochawesome來(lái)產(chǎn)生好看的HTML報(bào)告。

安裝 npm i mochawesome

把報(bào)告放到output目錄下

"mocha": {
  "reporterOptions": {
      "reportDir": "output"
  }
},

使用codeceptjs run --reporter mochawesome 來(lái)執(zhí)行測(cè)試產(chǎn)生html報(bào)告,最終的報(bào)告會(huì)被放在 output/mochawesome.html。

對(duì)于失敗的測(cè)試,我們需要有截圖,我們可以使用下面的配置來(lái)實(shí)現(xiàn)

"helpers": {
    "Mochawesome": {
        "uniqueScreenshotNames": "true"
    }
  },

Test Options

Feature()和Scenario()的第二個(gè)參數(shù)可以是一對(duì)鍵值對(duì),這個(gè)參數(shù)的作用是為測(cè)試場(chǎng)景提供一些選項(xiàng)比如timeout和retry

Feature('My feature', {key: val});

Scenario('My scenario', {key: val}, (I) => {});
Timeout

默認(rèn)狀態(tài)下,測(cè)試場(chǎng)景是沒(méi)有timeout的,我們可以通過(guò)傳遞下面的鍵值對(duì)來(lái)設(shè)置feature級(jí)別或是scenario級(jí)別的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測(cè)試是不穩(wěn)定的,有些時(shí)候我們需要rerun來(lái)讓相應(yīng)的測(cè)試場(chǎng)景通過(guò)。我們可以通過(guò)在Feature()和Scenario()中傳遞retries鍵值對(duì)來(lái)實(shí)現(xiàn)。


//所有scenario失敗后都會(huì)retry3次
Feature('Complex JS Stuff', {retries: 3})

//這個(gè)scenario失敗后只會(huì)retry1次
Scenario('Not that complex', {retries: 1}, (I) => {
  // test goes here
});
//這個(gè)scenario失敗后會(huì)retry3次
Scenario('Really complex', (I) => {
  // test goes here
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容