Webdriver.IO 起手式

by alincode

Java(5 years)

SDET

English

NodeJS

freelancer

freelancer / remote

full-time

alincode

5 years

8 months

7 months

8 months

框架介紹
快速開始測試專案

Getting Started

Live Demo

經驗分享

Learning Resources

Q and A

框架介紹

What

End to End

Testing Framework

WebdriverIO

!=

WebdriverJS

Protractor
NightwatchJS
WebdriverIO
Geb
Robot

NodeJS

底層架構

Web

Selenium restful API

Mobile

Appium (base on selenium)

Module is King

npm install wdio-junit-reporter --save-dev
// wdio.conf.js
module.exports = {
    // ...
    reporters: ['dot', 'junit'],
    reporterOptions: {
        junit: {
            outputDir: './'
        }
    },
    // ...
};

install module

setting config

BDD / TDD framework

Cloud service

Task Runner plugin

Reporter

Dot reporter

Spec reporter

JUnit reporter

Customer reporter

Teamcity reporter

sync and async

exports.config = {
    sync: true
};

Async (Promises based)

Sync

Promises based

describe('getTitle', () => {
    it('should return the url of the current webpage', async function () {
        (await this.client.getUrl()).should.be.equal(conf.testPage.start)
    })
})
client.url('http://webdriver.io')
      .getUrl().then(function(url) {
          console.log(url);
      });

ES2015

Promise

WebdriverIO

  • selenium restful api
  • anything is module
  • more familiar api
  • config setting tool

Protractor

  • selenium-webdriver
  • good for angularJs project
  • but...

Protractor

<div class="ng-binding">Hi my name is {{name}}</div>

var greeting = element(by.binding('name'));

無縫接軌 angular data bind

angularjs website

it('should have learn link.', function(done) {
    browser.get('http://www.angularjs.org');
    var myElement = element(by.css('.learn-link'))
    expect(myElement.getText()).to.eventually.equal(
      'Learn Angular in your browser for free!');
    done();
});

no angularjs website

it('no angularjs website', function(done) {
    browser.driver.get('http://webdriver.io/');
    var myElement = browser.driver.findElement(by.css('h2'));
    expect(myElement.getText()).to.eventually.equal(
      'Selenium 2.0 bindings for NodeJS');
    done();
  });

 好像可以在 config 調整,but...

What we can do?

自動化測試
爬蟲
自動化截圖

快速開始測試專案

初始化專案
建立設定檔
測試可被執行

create project

mkdir webdriverio-sandbox
cd webdriverio-sandbox
npm init -y

install package

npm i webdriverio -D

generate config tool

./node_modules/webdriverio/bin/wdio

多選,請按空白鍵

vi wdio.conf.js

capabilities: [{
  browserName: 'chrome'
}],
mochaOpts: {
  timeout: 60000
},

init folder

mkdir -p ./test/specs/
mkdir -p ./errorShots/

vi test/specs/test.js

var assert = require('assert');

describe('mokayo page', function() {
  it('should have the right title', function() {
    browser.url('http://blog.mokayo.com');
    var title = browser.getTitle();
    assert.equal(title, '教你所想學的,用眼樂讀 - blog.mokayo.com');
  });
});

執行

./node_modules/webdriverio/bin/wdio wdio.conf.js
npm run e2e-test

"scripts": {
  "e2e-test": "wdio wdio.conf.js"
}

OR

Getting Started

Mode

  • Standalone Mode
  • The WDIO Testrunner

Standalone Mode

The WDIO Testrunner

Config

exports.config = {
    specs: [
        'test/spec/**'
    ],
    capabilities: [{
        browserName: 'chrome'
    }, {
        maxInstances: 5,
        browserName: 'firefox',
        specs: [
            'test/ffOnly/*'
        ]
    },{
        browserName: 'phantomjs',
        exclude: [
            'test/spec/alert.js'
        ]
    }],
    sync: true,
    screenshotPath: 'shots',
    baseUrl: 'http://localhost:8080',
    waitforTimeout: 1000,  // Default timeout for all waitForXXX commands.
};

Config

exports.config = {
    // WebdriverCSS: https://github.com/webdriverio/webdrivercss
    // WebdriverRTC: https://github.com/webdriverio/webdriverrtc
    // Browserevent: https://github.com/webdriverio/browserevent
    plugins: {
        webdrivercss: {
            screenshotRoot: 'my-shots',
            failedComparisonsRoot: 'diffs',
            misMatchTolerance: 0.05,
            screenWidth: [320,480,640,1024]
        },
        webdriverrtc: {},
        browserevent: {}
    },

    framework: 'mocha'
    reporters: ['dot', 'allure'],

    // Hooks
    onPrepare: function (config, capabilities) {
    }
  
};

Group Test Specs

// wdio.conf.js
exports.config = {

    // define all tests
    specs: ['./test/specs/**/*.spec.js'],      

    // define specific suites
    suites: {
        login: [
            './test/specs/login.success.spec.js',
            './test/specs/login.failure.spec.js'
        ],
        otherFeature: []
    }
}
wdio wdio.conf.js --suite login
wdio wdio.conf.js --suite login,otherFeature

Browser Object

  • webdriver instance
  • global object

selectors

browser.click('h2.subheading a');
  • CSS Query Selector (建議使用)
  • Link Text
  • Partial Link Text
  • Element with certain text
  • Tag Name
  • Name Attribute
  • xPath (不建議使用)

 

Collection of elements

Single element

browser.element('h2');
browser.elements('h2');

API

  • Action
  • Property
  • Protocol
  • State
  • Utility
  • Mobile (略)

Action

click

browser.click('#myButton');

setValue

var input = browser.element('.input');
input.setValue('test123');

submitForm

browser.submitForm('#loginForm');

Property

getTitle

browser.getTitle();

getValue

browser.element('.input').getValue();

submitForm

browser.submitForm('#loginForm');

Protocol

element

browser.element('.h2');

elements

browser.elements('li');

url

browser.url('http://webdriver.io');

State

isEnabled

browser.isEnabled(selector);

isExisting

browser.isExisting(selector);

isSelected

browser.isSelected(selector);

Utility

waitForExist

browser.element('.notification').waitForExist(5000);

saveScreenshot

browser.saveScreenshot('front_page.png');

end

client
    .init()
    .url('http://google.com')
    .end();
    // ends session and close browser

Utility

pause

browser.pause(5000);

debug

browser.addCommand();

addCommand

browser.debug();

Page Object

霰彈式修改

(Shotgun Surgery)

function Page() {}

Page.prototype.open = function(path) {
  browser.url('/' + path)
}

module.exports = new Page();
// login.page.js
var Page = require('./page')

var LoginPage = Object.create(Page, {

    // define elements
    username: { get: function () { return browser.element('#username'); } },
    password: { get: function () { return browser.element('#password'); } },
    form:     { get: function () { return browser.element('#login'); } },
    flash:    { get: function () { return browser.element('#flash'); } },

    // define or overwrite page methods
    open: { value: function() {
        Page.open.call(this, 'login');
    } },

    submit: { value: function() {
        this.form.submitForm();
    } }
});

module.exports = LoginPage
// login.spec.js
var expect = require('chai').expect;
var LoginPage = require('../pageobjects/login.page');

describe('login form', function () {
    it('登入失敗', function () {
        LoginPage.open();
        LoginPage.username.setValue('foo');
        LoginPage.password.setValue('bar');
        LoginPage.submit();

        expect(LoginPage.flash.getText()).to.contain('Your username is invalid!');
    });

    it('登入成功', function () {
        LoginPage.open();
        LoginPage.username.setValue('tomsmith');
        LoginPage.password.setValue('SuperSecretPassword!');
        LoginPage.submit();

        expect(LoginPage.flash.getText()).to.contain('login success');
    });
});

Live Demo

  • first test

  • site map

  • auth

  • add page test case

經驗分享

by google testing blog

too slow
not reliable
duplicate

測試範圍如何切割

如何開始寫前端測試

測試計畫
測試規格書
撰寫測試程式

測試計畫

# 正常登入流程

使用者可從首頁點擊登入轉至登入頁,並可輸入帳號密碼,密碼驗證過後,頁面顯示登入成功。

# 正常登入流程

使用者輸入帳號密碼驗證後,發現密碼輸入錯誤,停留在登入頁,顯示登入錯誤原因。

# 正常登出流程

使用者可點擊登出按鈕,畢竟狀態真正為登出。

# 檢查所有從首頁,可正常連連結至分支頁

登入頁、分類頁、個人資訊頁、聯繫客服頁

# etc...

測試規格書

不要用 xPath

好讀

易維護

/html/body/section/div/section[2]/article/a[1]
$('.icon-search')

XPATH

CSS Query Selector

Learning Resources

getter channel

github issue

mokayo

good for java developer

 開放Q and A 時間