前端測試(上)

前端測試(上)

在前端項目中,前端測試並無被重視,緣由有不少,好比 學習/研發成本高團隊不夠重視,或者項目不合適等,在這裏咱們不去追究是什麼緣由致使這種現象,可是有一點我很肯定,形成這種緣由,還有一個更重要的緣由,就是 「意識不到位」,即便有不少同窗瞭解過單元測試,可是也不知道如何應用到 「項目」 中,針對這種現象,咱們從一個簡單卻很常見的小項目,來打開測試工程化冰山一角html

在刷題的過程當中,咱們常常會使用一個項目用於練習寫筆試題,好比排序,查找之類的算法題目前端

石器時代

新建一個工程,目錄以下node

├── index.js
├── index.html
└── src
    └── search.js
  • index.html - 基於瀏覽器運行,至少早期的筆試題目,基於瀏覽器便可;此例,引入search.jsindex.js便可
  • index.js - 用於寫測試代碼,好比 console.log(binarySearch([1],1)===0)
  • search.js - 用於寫查找相關的算法代碼,好比順序查找、插值查找等,在這裏我只寫二分法查找順序查找函數
/**
 * 二分法
 * @param {Array} arr 
 */
function binarySearch(arr, expected) {
  let start = 0
  let end = arr.length - 1
  while (start <= end) {
    let mid = parseInt(start + (end - start) / 2)
    let value = arr[mid]
    if (value === expected) {
      return mid
    } else if (value > expected) {
      end = mid
    } else {
      start = mid
    }
  }
  return -1
}
/**
 * 順序查找
 * @param {*} arr 
 * @param {*} expected 
 */
function sequentialSearch(arr, expected) {
  let i = 0
  while (i > arr.length) {
    let value = arr[i]
    if (value === expected) {
      return i
    }
    i++
  }
  return -1
}

OK,大功告成,把頁面拖到瀏覽器中直接運行,連服務器都省了~~!
當我準備爲我這個完美的項目鼓掌的時候,眼角瞟到個人座右銘,成爲一個專業的大前端,此時此刻 專業這個詞格外刺眼,做爲新世紀好青年,我怎麼可能讓別人質疑個人專業,因而我要繼續裝(入)逼(坑)git

青銅時代

  1. 使用npm建立工程,而且對模塊進行管理
  2. 使用git對項目倉庫以及迭代進行管理
  3. 載體nodejs環境代替瀏覽器環境,這樣能夠測試文件讀取等IO操做
.
├── node_modules
└── test
    └── test.js
└── src
    └── search.js
├── package.json
├── .gitignore
├── index.js

在 package.json 配置算法

{
    ....
    "scripts":{
        "test":"node test/test.js"
    }
}

對應 js的模塊 要改爲commonjs規範
search.js 調整npm

function binarySearch(){
    //todo
}
function sequentialSearch(){
    //todo
}
module.exports = {
  binarySearch,
  sequentialSearch
}

index.js 調整json

const { binarySearch,sequentialSearch } = require('./src/search')
module.exports = {
  binarySearch,
  sequentialSearch
}

test.js 調整,爲了讓提示更加明顯點,咱們嘗試讓描述更加豐富點數組

const { binarySearch,sequentialSearch } = require('../index')
console.log(‘二分查找: [1]的1在數組0位置上’,binarySearch([1],1)===0)
console.log(‘二分查找:[1,2,3]的1在數組0位置上’,binarySearch([1,2,3],1)===0)
console.log(‘二分查找:[1,2,3]的2在數組1位置上’,binarySearch([1,2,3],2)===0)

console.log(‘順序查找:[1]的1在數組0位置上’,sequentialSearch([1],1)===0)
console.log(‘順序查找:[1,2,3]的1在數組0位置上’,sequentialSearch([1,2,3],1)===0)
console.log(‘順序查找:[1,2,3]的2在數組1位置上’,sequentialSearch([1,2,3],2)===0)

一頓操做猛如虎以後,感受完美的一筆~~
我火燒眉毛,運行 npm run testpromise

二分查找:[1]的1在數組0位置上 true
二分查找:[1,2,3]的1在數組0位置上 true
二分查找:[1,2,3]的2在數組1位置上 false
順序查找:[1]的1在數組0位置上 false
順序查找:[1,2,3]的1在數組0位置上 false
順序查找:[1,2,3]的2在數組1位置上 false

咱們發現 有幾點不足瀏覽器

  1. 當測試用例增長時,測試代碼變的難以管理,全部測試輸出揉在一塊兒,沒有分組的管理
  2. 無論成功或者失敗,沒有高亮顯示,也沒有顏色上的區分
  3. 即便錯誤,也沒有把錯誤詳細打印出來
  4. 沒有相關輸出報表

黃金時代

爲了解決 青銅時代 遺留下很多體驗問題,咱們不得不封裝一些方法,強化console的輸出,文檔輸出,可視化等輸出,然而咱們所作的一切強化,都是新概念 測試框架的雛形,不過在正式介紹 測試框架前,咱們先了解下 斷言

「我×,測試框架?斷言?這尼瑪又是什麼?」

斷言是單元測試中用來保證最小單元是否正常的檢測方法,用於判斷邏輯執行是否達到開發者預期的表達式,斷言在運行的過程當中,若斷言不爲真,程序會停止運行

「經常使用的斷言庫有哪些?」
  • assert - nodejs的內置核心模塊,node環境能夠直接使用
  • shouldjs - 基於assert模塊進行封裝擴展
  • expectjs - 基本是 shouldjs 的縮水版
  • chai - 目前比較流行的斷言庫,支持 TDD(assert),BDD(expect、should)兩種風格

咱們先簡單學習 assert, 做爲Nodejs內置核心模塊,無需引用,最爲 斷言 入門庫最爲合適

## assert

var assert=require('assert')
assert.equal(Math.max(1,100),100)

一旦 assert.equal()不知足指望,將會拋出AssertionError異常,整個程序將會中止運行

經常使用的檢測方法
  • ok(actual) - 判斷結果是否爲真
  • strictEqual(actual,expected,[,message]) - 判斷實際值和指望值是否嚴格相等
  • deepStrictEqual(actual, expected[, message]) -判斷實際值和指望值是否深度嚴格相等
  • doesNotReject(asyncFn, error) - 判斷代碼塊是否返回reslove
  • rejects(block, error)- 判斷結果返回reject
  • throws(block, error)- 判斷結果是否拋出異常
  • ifError()- 判斷實際值是否爲一個假值(null,undefined,0,'',false);若是爲真值,就會拋出異常
  • fail([message]) - 直接報錯
「感受腦袋疼,能不能通俗點?」
先來一個例子,壓壓驚,咱們把青銅時代的代碼優化下
console.log(‘順序查找:[1]的1在數組0位置上’,sequentialSearch([1],1)===0)
 //爲了通用性,咱們把sequentialSearch([1],1)===0 提煉出來
 function equal(actual,expected,message){
    return actual===expected?message:`${actual}!==${expected}`
 }
  console.log(‘順序查找:[1]的1在數組0位置上’,equal(sequentialSearch([1],1),0,'成功'))

通俗的說 就是 equal這個方法就是斷言

「我迷迷糊糊的貌似明白了一點,那我運行一下嚐嚐鮮吧」

test/index.js

const assert = chai.assert
const { binarySearch } = require('../index')

assert.equal(binarySearch([1], 1), 0)//成功
assert.equal(binarySearch([1], 1), 1)//失敗
assert.equal(binarySearch([1,2], 2), 1)//成功
assert.equal(binarySearch([1,2], 1), 0)//失敗

運行 node test/index.js

//失敗輸出
AssertionError: expected 0 to equal 1
    at Object.<anonymous> (F:\learn\test\index.js:19:8)
「呃....我以爲這體驗,也青銅時代差很少」

咱們能夠看到,在第二個測試用例執行時,發現代碼執行失敗後,直接退出程序,同時提示你 指望值實際運行值,以及對於錯誤代碼相關提示等等。錯誤提示方面比封裝equire方法強大很多;可是,依舊不能讓我願意使用它。

  1. 成功時,沒有任何提示
  2. 雖然有錯誤提示,可是運行到第一個錯誤的時候,就程序退出;開發者沒法看到 本身的測試用例 錯誤多少個
  3. 依舊沒有高亮,可視化方面依舊蒼白

沒錯,斷言拿到很是重要錯誤信息;可是他沒有解決體驗問題;若是說 斷言是裏子,那測試框架 就是面子

「測試框架是什麼?」

測試框架 通俗的說就是專門 服務於代碼塊測試解決方案,他主要有如下功能

  • 管理測試用例
  • 生成測試報告
「經常使用的測試框架有哪些?」
  • [jasmine]() -自帶斷言(assert),mock 功能
  • [mocha]() -框架不帶斷言和mock功能,須要結合其餘工具

通俗的說,測試框架 就是 管理/執行斷言,他和斷言一塊兒使用將會更增強大

Mocha

mocha 是一款強大的測試框架,可以運行在nodejs和瀏覽器中,可以高效的管理測試用例,支持多種測試報告格式

  • 支持多種斷言庫:chai/shouldjs/expectjs/assert
  • 支持兩種測試風格:TDD/BDD

    • TDD:基於測試用例 進行測試
    • BDD:基於產品自己功能 進行測試
經常使用方法
  1. describe(string,callback) -主要用於對測試用例的分組,層級描述。TDD使用suite
  2. it(string [,callback])-測試用例,callback 包含一個或者多個斷言;當callback不存在時,表示這個測試用例須要寫,但目前還未寫入,狀態使用pending表示
  3. hook-用於協助describe中測試用例的準備·安裝·卸載和回收等工做,Hook通常用於describe內,但也能夠describe外,做爲頂級Hook

    • before/after([ string ,]callback) - 分別在進入或者退出describe時觸發執行
    • beforeEach/afterEach([ string ,]callback) - 分別在describe中每一個測試用例執行前和執行後觸發執行

Full example
test/index.js

describe('hooks', function() {
  before(function() {
    console.log('before')
  });

  after(function() {
    console.log('after')
  });

  beforeEach(function() {
    console.log('beforeEach')
  });

  afterEach(function() {
    console.log('afterEach')
  });
  it('Test1',()=>{
    console.log('test1')
  })
  it('Test2',()=>{
    console.log('test2')
  })
  // test cases
});

運行 npm run test

{
    "script":{
        " test":"mocha"
    }
}

hooks
before
beforeEach
test1

√ Test1

afterEach
beforeEach
test2

1) Test2

afterEach
after

1 passing (15ms)
1 failing

1) hooks

Test2:

  AssertionError: expected 0 to equal 1
  + expected - actual

  -0
  +1

  at Context.it (test\index.js:93:12)
咱們能夠看到 基於mocha後的斷言,他的提示體驗大大的提高
- 成功後,有相關提示
- 遇到失敗時,依舊能夠執行下去,展現全部失敗用例信息
- 統計測試用例成功數和失敗數
4. **異步處理** 
      - done
it('should save without error', (done)=> {
    var user = new User('Luna');
    user.save((err)=> {
      if (err) done(err);
      else done();
    });
    //user.save(done);
  });

  ```
  - promise
  ```ts
  it('respond with matching records', ()=> {
    return db.find({type: 'User'}).should.eventually.have.length(3);
  });
  ```
  - async/await
  ```ts
  it('responds with matching records', async function() {
    const users = await db.find({type: 'User'});
    users.should.have.length(3);
  });
  ```
    1. Only-屏蔽其餘測試單元/測試用例,只執行標識爲Only的測試單元/用例。通常用於 當你的單元測試越寫越多時,只想測試新寫的單元測試是否正確,這個屬性就能夠幫你在執行時,幫你過濾掉其餘測試,加快執行速度

      describe.only('something', function() {
        // 只會跑包在裏面的測試
      })

      或者

      it.only('do do', () => {
        // 只會跑這一個測試
      })
    1. skip-表示執行時,跳過標識的測試單元/測試用例,能夠做用於describeit

      it.skip('should return -1 unless present', function() {
        // 代碼不存被執行
      });
      
      it('should return the index when present', function() {
        // 代碼會執行
      });

      能夠this.skip()在測試用例執行的時候,根據運行時過濾當前測試案例

      describe('outer', function() {
        before(function() {
          this.skip();
        });
      
        after(function() {
          // will be executed
        });
      
        describe('inner', function() {
          before(function() {
            // will be skipped
          });
      
          after(function() {
            // will be skipped
          });
        });
      });
    2. 其餘
    • timeout - 超時
    相關文章
    相關標籤/搜索