前端自動化測試解決方案探析

main188 8年前發布 | 9K 次閱讀 自動化測試 前端技術

前端測試一直是前端項目開發過程中機器重要的一個環節,高效的測試方法可以減少我們進行代碼自測的時間,提高我們的開發效率,如果你的代碼涉及的測試用例較多,而且項目需要長期維護,這時就可以考慮使用一下自動化測試了。

一、前端自動化測試

前端自動化測試一般是指是在預設條件下運行前端頁面或邏輯模塊,評估運行結果。預設條件應包括正常條件和異常條件,以達到自動運行測試過程、減少或避免人工干預測試的目的。在前端自動化測試中,我們通常是通過不同的工具來解決不同場景下不同的問題的。就測試類型來看,主要分為BDD(Bebavior Driven Developement,行為驅動測試)和TDD(Testing Driven Developement,測試驅動開發)。BDD可以讓項目成員(甚至是不懂編程的)使用自然描述語言來描述系統功能和業務邏輯,從而根據這些描述步驟進行系統自動化的測試;TDD則要求在編寫某個功能的代碼之前先編寫測試代碼,然后只編寫使測試通過的功能代碼,通過測試來推動整個開發的進行。這有助于編寫簡潔可用和高質量的代碼,并加速實際開發過程

BDD和TDD均有各自的適用場景,BDD一般更偏向于系統功能和業務邏輯的自動化測試設計,而TDD在快速開發并測試功能模塊的過程中則更加高效,以快速完成開發為目的。下面我們看下BDD和TDD具體的特點:

BDD的特點: - 從業務邏輯的角度定義具體的輸入與預期輸出,以及可衡量的目標; - 盡可能覆蓋所有的測試用例情況; - 描述一系列可執行的行為,根據業務的分析來定義預期輸出。例如,expect, should, assert; - 設定關鍵的測試通過節點輸出提示,便于測試人員理解; - 最大程度的交付出符合用戶期望的產品,避免輸出不一致帶來的問題。

TDD的特點: - 需求分析,快速編寫對應的輸入輸出測試腳本; - 實現代碼讓測試為成功; - 重構,然后重復測試,最終讓程序符合所有要求。

二、單元測試解決方案

就前端而言,單元測試的實現工具比較多。主要有mocha,jasmine和qunit。我們先來看看使用mocha是怎樣實現單元測試的。

  • mocha

mocha的特點是簡單可擴展、支持瀏覽器和Node、支持同步和異步、支持連續用例測試。測試集,以函數describe(string, function)封裝;測試用例,以it(string, function)函數封裝,它包含2個參數;斷言,以assert語句表示,返回true或false。另外,mocha在完成異步測試用例時通過done()來標記。

$ npm install mocha
$ mkdir test
$ $EDITOR test/test.js # or open with your favorite editor

測試用例:

var assert = require('assert');
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(-1, [1,2,3].indexOf(4));
    });
  });
});

輸出為:

$ ./node_modules/mocha/bin/mocha

  Array
    #indexOf()
      ? should return -1 when the value is not present


  1 passing (9ms)

同時,mocha支持異步和Promise。

describe('#find()', function() {
    it('respond with matching records', function(done) {
        db.find({type: 'User'}, function(err, res) {
            if (err) return done(err);
            res.should.have.length(3);
            done();
            });
        });
    });
});
  • jasmine。

jasmine是一個BTT的框架,不依賴其它框架。測試集以函數describe(string, function)封裝;測試用例,以it(string, function)函數封裝,它也包含2個參數;斷言,以expect語句表示,返回true或false;斷言的比較操作時,將Expectation傳入的實際值和Matcher傳入的期望值比較,另外任何Matcher都能通過在expect調用Matcher前加上not來實現一個否定的斷言(expect(a).not().toBe(false);)

describe("A suite is just a function", function() {
    var a;
    it("and so is a spec", function() {
        a = true;

        expect(a).toBe(true);
        expect(a).not().toBe(false);
    });
});

jasmine也支持異步測試用例。

describe("long asynchronous specs", function() {
    beforeEach(function(done) {
    done();
}, 1000);

it("takes a long time", function(done) {
    setTimeout(function() {
        done();
    }, 9000);
}, 10000);

afterEach(function(done) {
    done();
    }, 1000);
});
  • qunit

qunit是一個可基于jquery的簡單測試框架,主要運行在瀏覽器端。它通過QUnit.test定義一個測試集,一個測試集中通過回調函數里面多個斷言判斷來實現多個測試用例,使用起來非常簡單。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>QUnit Example</title>
    <link rel="stylesheet" >
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
    <script src="https://code.jquery.com/qunit/qunit-2.0.1.js"></script>
    <script>
    QUnit.test( "hello test", function( assert ) {
        assert.ok( 1 == "1", "Passed!" );
        assert.equal( null, false, "null, false; equal fails" );
    });
    </script>
</body>
</html>

qunit也支持異步測試用例,異步完成時通過done()來結束。

QUnit.test( "assert.async() test", function( assert ) {
    var done = assert.async();
    var input = $( "#test-input" ).focus();
    setTimeout(function() {
        assert.equal( document.activeElement, input[0], "Input was focused" );
        done();
    });
});

小結一下,單元測試工具的主要組成部分其實是類似的,主要包括測試集、測試用例、斷言和斷言比較等。它可以用來快速測試單元模塊的主要功能,有助于輔助我們快速開發。

三、集成化測試解決方案

除了模塊單元的測試驅動開發,在系統功能測試階段,我們希望自動化完成業務功能正確性的檢測,此時我們就要考慮集成測試方案了。目前前端集成化測試自動化工具也有比較多。例如CasperJS、Nighmare、Nightwatch、Dalekjs,我們來逐個看下。

  • casperJS。

casperJS基于PhantomJS或SlimerJS(PhantomJS或SlimerJS都是用于web測試的自動化無界面瀏覽器),可以模擬完成頁面內系統級的自動化操作行為測試。

var casper = require('casper').create();
casper.start('http://casperjs.org/');

casper.then(function() {
    this.echo('First Page: ' + this.getTitle());
});

casper.thenOpen('http://phantomjs.org', function() {
    this.echo('Second Page: ' + this.getTitle());
});

casper.run();

輸出內容為:

$ casperjs sample.js
First Page: CasperJS - a navigation scripting & testing utility for PhantomJS and SlimerJS written in Javascript
Second Page: PhantomJS | PhantomJS

頁面內的操作結合casper的操作就可以這樣來實現。

var casper = require('casper').create();
var links;

function getLinks() {
// Scrape the links from top-right nav of the website
    var links = document.querySelectorAll('ul.navigation li a');
    return Array.prototype.map.call(links, function (e) {
        return e.getAttribute('href')
    });
}

// Opens casperjs homepage
casper.start('http://casperjs.org/');

casper.then(function () {
    links = this.evaluate(getLinks);
});

casper.run(function () {
    for(var i in links) {
        console.log(links[i]);
    }
    casper.done();
});
  • Nightmare。

類似的,nightmare也是一個模擬還原瀏覽器上業務操作的強大工具,而且更易于使用。同時可以使用chrome的插件daydreem自動錄制生成用戶行為操作的事件序列,更加方便我們進行實際的測試。

yield Nightmare()
    .goto('http://yahoo.com')
    .type('input[title="Search"]', 'github nightmare')
    .click('.searchsubmit');

Nightmare也支持異步操作,并支持多種斷言庫,這里結合chai.js就可以這樣來使用。

var Nightmare = require('nightmare');
var expect = require('chai').expect; // jshint ignore:line

describe('test yahoo search results', function() {
    it('should find the nightmare github link first', function(done) {
    var nightmare = Nightmare()
    nightmare
      .goto('http://yahoo.com')
      .type('form[action*="/search"] [name=p]', 'github nightmare')
      .click('form[action*="/search"] [type=submit]')
      .wait('#main')
      .evaluate(function () {
        return document.querySelector('#main .searchCenterMiddle li a').href
      })
      .end()
      .then(function(link) {
        expect(link).to.equal('https://github.com/segmentio/nightmare');
        done();
      })
  });
});
  • Nightwatch。

Nightwatch則可以使用node書寫端對端的測試用例,并在Selenium server服務端運行測試,同樣支持同步和異步。

this.demoTestGoogle = function (browser) {
  browser
    .url('http://www.google.com')
    .waitForElementVisible('body', 1000)
    .setValue('input[type=text]', 'nightwatch')
    .waitForElementVisible('button[name=btnG]', 1000)
    .click('button[name=btnG]')
    .pause(1000)
    .assert.containsText('#main', 'The Night Watch')
    .end();
};
  • Dalekjs

DalekJS是一個跨瀏覽器平臺的前端集成測試框架,可以自動配置啟動本地的瀏覽器,也可以模擬填寫提交表單、點擊、截屏、運行單元測試等豐富的操作。

module.exports = {
    'Amazon does its thing': function (test) {
      test
        .open('http://www.amazon.com/')
        .type('#twotabsearchtextbox', 'Blues Brothers VHS')
        .click('.nav-submit-input')
        .waitForElement('#result_0')
        .assert.text('#result_0 .newaps a span').is('The Blues Brothers')
        .done();
    }
};
test.open('http://adomain.com')
    .click('#aquestion')
    .answer('Rose')
    .assert.text('#aquestion').is('Rose', 'Awesome she was!')
    .done();

小結一下,和單元測試相同的是,集成測試和單元測試類似,一般也會對測試預期輸出進行斷言和判斷,不同的是,集成測試的輸入設計和功能流程中涉及到瀏覽器本身的行為模擬,用以代替測試人員手動操作的過程,從而能夠提高測試效率。

四、總結與注意事項

通過對單元測試工具和集成測試工具的概述介紹,我們基本了解了單元測試和集成測試的核心部分和特點,盡管目前主流的測試工具各不相同,但是基本的流程原理確實相同的,上面小結里面也為大家做了分析。

當然,還有一些仍需要我們注意的問題。自動化測試不可避免地要求我們去編寫測試用例,會花去一定的事件,我們在實際的項目開發過程中,決定要不要使用自動化的測試方案應該根據具體的場景來決定,如果業務規模并不復雜,而且系統功能流程清晰,則不建議使用測試用例,因為這樣得不償失;但如果業務達到一定規模,需要在原有較大項目繼續維護開發的情況下,編寫測試用例有利于我們較快暴露和定位問題,并極有助于后期的維護。

參考資料:

http://joshldavis.com/2013/05/27/difference-between-tdd-and-bdd/

https://pythonhosted.org/behave/philosophy.html

http://wiki.c2.com/?TestDrivenDevelopment

 

來自:http://jixianqianduan.com/frontend-javascript/2016/11/22/front-end-auto-test.html

 

 本文由用戶 main188 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!