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

  • 2019 年 12 月 4 日
  • 筆記

本文作者:IMWeb ouven 原文出處:IMWeb社區 未經同意,禁止轉載

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

一、前端自動化測試

  前端自動化測試一般是指是在預設條件下運行前端頁面或邏輯模塊,評估運行結果。預設條件應包括正常條件和異常條件,以達到自動運行測試過程、減少或避免人工干預測試的目的。在前端自動化測試中,我們通常是通過不同的工具來解決不同場景下不同的問題的。就測試類型來看,主要分為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();              });          });      });  });

http://mochajs.org/

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

https://jasmine.github.io/2.5/introduction

  • 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" href="https://code.jquery.com/qunit/qunit-2.0.1.css">  </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();      });  });

http://api.qunitjs.com/async/

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

三、集成化測試解決方案

  除了模塊單元的測試驅動開發,在系統功能測試階段,我們希望自動化完成業務功能正確性的檢測,此時我們就要考慮集成測試方案了。目前前端集成化測試自動化工具也有比較多。例如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();  });

http://casperjs.org/

  • 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();        })    });  });

http://www.nightmarejs.org/

  • 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();  };

http://nightwatchjs.org/

  • 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://dalekjs.com/

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

四、總結與注意事項

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

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

參考資料:

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

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

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