基於node.js的接口自動化測試


為什么要自動化測試

就我個人而言,目前需要測試的有3條產品線,測試范圍包括web端、APP端。一輪測試下來,測試所花費的時間是極大的,隨之而來問題也就來了:加班測試、版本發布時間受阻、考慮不周全而漏測功能等等。

粗略算了一下,假設半月迭代一次,每次迭代需要5輪測試,人工回歸一次就需要5個小時,最終確定一年下來,自動化為你省去600個小時,也就是75個工作日,同時也省去了測試舊功能要吐的煩惱以及人疲憊下產生的錯誤。

那么在這種情況下,就開始了我的基於node.js自動化測試之旅,所以就有了此篇文章,如有不足之處,還望各位大佬批評指正。

 

Mocha初識

因為后面要用到mocha框架,大家先了解下什么是mocha。

mocha 提供 TDD(測試驅動開發)、BDD (行為驅動開發) 和 exports 風格的接口。

 

BDD風格

BDD是“行為驅動的開發”(Behavior-Driven Development)的簡稱。BDD認為,不應該針對代碼的實現細節寫測試,而是要針對行為寫測試。BDD測試的是行為,即軟件應該怎樣運行。

BDD接口提供以下方法:

  • describe():測試套件
  • it():測試用例
  • before():所有測試用例的統一前置動作
  • after():所有測試用例的統一后置動作
  • beforeEach():每個測試用例的前置動作
  • afterEach():每個測試用例的后置動作

BDD的特征就是使用describe()和it() 這兩個方法

before()、after()、beforeEach()和afterEach() 是為測試做輔助的作用域,它們合起來組成了hook的概念。

 

describe()和it()

 
describe()

describe()方法接收兩個參數:第一個參數是一個字符串,表示測試套件的名字或標題,表示將要測試什么。第二個參數是一個函數,用來實現這個測試套件。

上面引出了一個概念:測試套件。那什么是測試套件呢?

測試套件(test suite)指的是,一組針對軟件規格的某個方面的測試用例。也可以看作,對軟件的某個方面的描述(describe)。結構如下:

describe("A suite", function() {
    // ...
});
 
it()

要想理解it(),首先我們要知道什么是測試用例? 測試用例(test case)指的是,針對軟件一個功能點的測試,是軟件測試的最基本單位。一組相關的測試用例,構成一個測試套件。

測試用例由it函數構成,它與describe函數一樣,接受兩個參數:第一個參數是字符串,表示測試用例的標題;第二個參數是函數,用來實現這個測試用例。

 
BDD風格用例
//模塊依賴
var assert = require("assert");

describe('Array', function(){   //測試套件
    describe('#indexOf()', function(){
        it('當值不存在時應該返回 -1', function(){   //測試用例
        assert.equal(-1, [1,2,3].indexOf(5)); //斷言條件
        assert.equal(-1, [1,2,3].indexOf(0));
        });
    });
});
 

TDD風格

TDD(測試驅動開發)組織方式是使用測試集(suite)和測試(test)。

每個測試集都有 setup 和 teardown 函數。這些方法會在測試集中的測試執行前執行,它們的作用是為了避免代碼重復以及最大限度使得測試之間相互獨立。

TDD接口提供以下方法:

  • suite:類似BDD中 describe()
  • test:類似BDD中 it()
  • setup:類似BDD中 before()
  • teardown:類似BDD中 after()
  • suiteSetup:類似BDD中 beforeEach()
  • suiteTeardown:類似BDD中 afterEach()
 

示例

var assert = require("assert");

suite('Array', function(){
    setup(function(){
        console.log('測試執行前執行');
    });

suite('#indexOf()', function(){
    test('當值不存在時應該返回 -1', function(){
        assert.equal(-1, [1,2,3].indexOf(4));
        });
    });
});

運行mocha:

mocha --ui tdd .js (表示的是文件名)

PS:mocha 默認是使用 bdd 的接口,所以在這里我們告訴mocha我們用的是tdd.
 

hook機制

hook 就是在測試流程的不同時段觸發,比如在整個測試流程之前,或在每個獨立測試之前等。

hook也可以理解為是一些邏輯,通常表現為一個函數或者一些聲明,當特定的事件觸發時 hook 才執行。

提供方法有:before()、beforeEach() after() 和 afterEach()。

方法解析:

  • before():所有測試用例的統一前置動作
  • after():所有測試用例的統一后置動作
  • beforeEach():每個測試用例的前置動作
  • afterEach():每個測試用例的后置動作

用法:

describe('hooks', function() {
    before(function() {
        //在執行本區塊的所有測試之前執行
    });

    after(function() {
        //在執行本區塊的所有測試之后執行
    });

    beforeEach(function() {
        //在執行本區塊的每個測試之前都執行
    });

    afterEach(function() {
        //在執行本區塊的每個測試之后都執行
    });

        //測試用例
});
 

異步測試

Mocha默認每個測試用例最多執行2000毫秒,如果到時沒有得到結果,就報錯。對於涉及異步操作的測試用例,這個時間往往是不夠的,需要用-t或--timeout參數指定超時門檻。

it('測試應該5000毫秒后結束', function (done) {
    let x = true
    let f = function () {
        x = false
        expect(x).to.be.not.ok
        done() // 通知Mocha測試結束
    }
   setTimeout(f, 4000)
})

上面的測試用例,需要4000毫秒之后,才有運行結果。所以,需要用-t或--timeout參數,改變默認的超時設置。

$ mocha -t 5000 timeout.test.js

上面命令將測試的超時時限指定為5000毫秒。

另外,上面的測試用例里面,有一個done函數。it塊執行的時候,傳入一個done參數,當測試結束的時候,必須顯式調用這個函數,告訴Mocha測試結束了。否則,Mocha就無法知道,測試是否結束,會一直等到超時報錯。

 

斷言

mocha支持任何可以拋出一個錯誤的斷言模塊。例如:should.js、better-assert、expect.js、chai等。這些斷言庫各有各的特點,大家可以了解一下它們的特點,根據使用場景來選擇斷言庫。

 

assert斷言(詳細說明)

斷言(assert)指的是對代碼行為的預期。一個測試用例內部,包含一個或多個斷言(assert)。

assert斷言會返回一個布爾值,表示代碼行為是否符合預期。測試用例之中,只要有一個斷言為false,這個測試用例就會失敗,只有所有斷言都為true,測試用例才會通過。

例如

assert.equal(-1, [1,2,3].indexOf(5));
assert.equal(-1, [1,2,3].indexOf(0));

實際值(-1)和期望值([1,2,3].indexOf(5))是一樣的,斷言為true,所以這個測試用例成功了。

mocha允許開發者使用任意的斷言庫,當這些斷言庫拋出了一個錯誤異常時,mocha將會捕獲並進行相應處理。下面是一些適用於Node.js或瀏覽器的斷言庫:

  • should.js
  • expect.js
  • chai.js
  • better-assert
  • assert:node.js原生模塊。
 

chai斷言

Chai 是一個非常靈活的斷言庫,它可以讓你使用如下三種主要斷言方式的任何一種:

 
assert:

這是來自老派測試驅動開發的經典的assert方式。比如:

assert.equal(variable, "value");

 
expect:expect語法

這種鏈式的斷言方式在行為驅動開發中最為常見。比如:

expect(variable).to.equal("value");

 
should:

這也是在測試驅動開發中比較常用的方式之一。舉例:

variable.should.equal("value");

 

一點技巧

 

僅執行指定測試

大型項目有很多測試用例,有時,我們希望只運行其中的幾個,這時可以用.only()方法。describe塊和it塊都允許調用.only()方法,表示只運行某個測試套件或測試用例。

describe('Array', function(){
    describe.only('#indexOf()', function(){
        ...
    })
})

或一個指定的測試用例:

describe('Array', function(){
    describe('#indexOf()', function(){
        it.only('當值不存在時應該返回 -1', function(){
            ...
        })

        it('當值不存在時應該返回 -1', function(){
            ...
        })
    })
})
 

忽略某個測試

該特性和 .only()非常相似,通過添加 .skip() 你可以告訴 Mocha 忽略的測試套件或者測試用例(可以有多個)。該操作使得這些操作處於掛起的狀態,這比使用注釋來的要好,因為你可能會忘記把注釋給取消掉。

describe('Array', function(){
    describe.skip('#indexOf()', function(){
        ...
    })
})

或一個指定的測試用例:

describe('Array', function(){
    describe('#indexOf()', function(){
        it.skip('當值不存在時應該返回 -1', function(){
            ...
        })

        it('當值不存在時應該返回 -1', function(){
            ...
        })
    })
})
 

環境准備

  • 操作系統:Windows、Mac
  • 代碼編輯器:隨個人喜好,推薦使用Visual Studio Code
  • node.js 官網:https://nodejs.org/en/
  • 安裝配置教程:傳送門
 

具體步驟

  • 下載Visual Studio Code,node.js, node.js使用最新版或者穩定版都可以。

  • 安裝完成后,打開命令行 輸入node --version 或 npm --version 可以看到具體版本即可,例如我本機安裝版本為:

    $ node --version

    v8.11.4

    $ npm --version

    5.6.0

  • 建立一個名為exam的項目(文件夾),再建一個為test的下級目錄,將編寫好的測試文件放到此目錄下

  • 模塊安裝

    • 在項目位置打開命令提示符,或使用VS code自帶的終端,輸入npm install mocha命令安裝mocha,或輸入cnpm install mocha、yarn add mocha(也可簡寫為npm i 模塊名/cnpm i 模塊名/yarn add 模塊名)

    • 繼續輸入cnpm i或yarn add命令安裝其他模塊(可能會出現安裝失敗的情況,需要多裝幾次)

注:cnpm安裝方法npm i cnpm;yran安裝方法npm i yarn

全局安裝命令:npm install -g mocha

作為項目的依賴安裝命令:npm install --save-dev mocha

  • 作為一個新的Node.js項目,先執行npm init 創建 package.json文件,創建時會要求輸入項目信息,根據實際情況進行填寫即可

  • 在package.json文件中設置一個測試腳本:

    "scripts":{
        "test": "mocha"
    },
    
 

腳本的編寫

  • 全局定義(用到什么定義什么)

    /* global describe */
    
    /* global before */
    
    /* global beforeEach */
    
    /* global it */
    
  • 導包、引模塊(需要什么導什么)

    let $ = global.$ = require('meeko')
    let assert = require('assert')
    let req = require('co-request')
    const Pro = require('../config')
    const db = global.db = require('j2sql')(Pro.mysql)
    const api = require('../models/api')
    const url = 'http://127.0.0.1:16001'
    const crypto = require('crypto')
    const transSql = require('../models/utils')
    let con = require('./con')
    
  • 這里是測試之前的准備工作(獲取token)和部分腳本

    describe('接口測試', async function () {
        before(async function () {
            await $.wait(1500)
    	            let r = (await req['post']({
    	                url: url + '/account/login',
    	                form: {
    	                    unicode: con.unicode // 微信登錄唯一id
    	                }
    	            })).body
    	            r = JSON.parse(r)
    	            // console.log(r)
    	            token = r.data.token
    	            timestamp = +new Date()
    	            sign = signHM(timestamp, token)
    	        })
    	        beforeEach(async function () {
    	            await $.wait(50)
        })
    
        // 這是登錄接口的部分腳本
        describe('/account/login          post  微信登錄游戲', async function () {
            it('登錄失敗,缺少unicode', async function () {
                let r = (await req['post']({
                url: url + '/account/login',
                form: {
                    unicode: '' // 微信登錄唯一id
                }
                })).body
                r = JSON.parse(r)
                // console.log(r)
                assert.strictEqual(r.msg, '登錄失敗')
            })
            it('登錄失敗,unicode類型錯誤', async function () {
                let r = (await req['post']({
                url: url + '/account/login',
                form: {
                    unicode: con.typeErr // 類型錯誤
                }
                })).body
                r = JSON.parse(r)
                // console.log(r)
                assert.strictEqual(r.msg, '登錄失敗')
            })
            it('登錄失敗,方法類型不對', async function () {
                let r = (await req['get']({
                url: url + '/account/login',
                form: {
                    unicode: '' // 微信登錄唯一id
                }
                })).body
                r = JSON.parse(r)
                // console.log(r)
                assert.strictEqual(r.msg, '方法類型不對')
            })
        })
    })
    

在上面的腳本中,使用了BDD的方式來進行測試,即describe()測試套件 + it()測試用例 + assert斷言方式。比較簡單,還望各位大佬多多指教。

 

運行

如果在package.json文件中設置測試腳本,那么我們直接在終端輸入npm test即可運行(如出現缺少模塊的情況,按提示一個一個安裝即可)

 

測試報告

  • 如果想查看測試報告,先在“擴展”中搜索“Live Server”並添加

  • 使用命令npm install --save-dev mochawesome安裝 mochawesome,然后在package.json文件中添加:

    "scripts": {
        "test": "mocha --reporter mochawesome"
    },
    
  • 最后打開mochawesome-report文件夾,右擊mochawesome.html選擇Open with Live Server

 

持續集成

我們將測試腳本寫好后,通過gitlab配置鈎子,將代碼上傳到gitlab后,就能實現自動化測試了

 

幾個需要注意的文件

 

config.js

配置文件,包括了本地開發環境、測試環境和生產環境

環境里包含了數據庫、Redis、微信公眾號、文件上傳下載等等等等。

 

package.json

 

devDependencies 和 dependencies

dependencies 存放項目或組件代碼中依賴到的

devDependencies 存放測試代碼依賴的包或構建工具的包

npm install 【依賴】或 npm install 【依賴】--save會把依賴放到dependencies下,表示代碼運行時所需要的包。

npm install 【依賴】--save-dev 會把依賴放到devDependencies下,表示開發時依賴的插件(即不會打包至線上)。

 
安裝依賴
  • 如果拿到別人的項目,需要安裝之前package.json中devDependencies 和 dependencies兩個模塊下所列舉的依賴,可以通過執行以下命令實現

    npm install
    
  • 如果拿到別人的項目,只需要安裝之前package.json中dependencies 模塊下所列舉的依賴,可以通過執行以下命令實現

    npm install packagename
    
  • 如果拿到別人的項目,只需要安裝之前package.json中devdependencies 模塊下所列舉的依賴,可以通過執行以下命令實現

    npm install packagename -dev
    
 
刪除依賴
  • npm uninstall "依賴名稱":刪除依賴,但不會刪除package.json的配置(即通過npm install依然可以安裝該依賴),刪除mocha依賴實例代碼如下

    npm uninstall mocha
    
  • npm uninstall "依賴名稱"  --save-dev:刪除依賴,同時刪除package.json中devdependencies 的配置,刪除mocha依賴實例代碼如下

    npm uninstall mocha  --save-dev
    
  • npm uninstall "依賴名稱"  --save:刪除依賴,同時刪除package.json中dependencies 的配置,刪除mocha依賴實例代碼如下

    npm uninstall mocha --save


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM