前端單元測試-jest

測試分爲三個種類

  • 單元測試
    在計算機編程中,單元測試(英語:Unit Testing)又稱爲模塊測試, 是針對程序模塊(軟件設計的最小單位)來進行正確性檢驗的測試工做。程序單元是應用的最小可測試部件。在過程化編程中,一個單元就是單個程序、函數、過程等;對於面向對象編程,最小單元就是方法,包括基類(超類)、抽象類、或者派生類(子類)中的方法
  • 集成測試
    集成測試,也叫組裝測試或聯合測試。在單元測試的基礎上,將全部模塊按照設計要求(如根據結構圖)組裝成爲子系統或系統,進行集成測試。
  • 功能測試
    功能測試就是對產品的各功能進行驗證,根據功能測試用例,逐項測試,檢查產品是否達到用戶要求的功能。

測試的好處

  • 減小bug
  • 提升代碼質量
  • 快速定位問題,減小調試時間
  • 放心重構

缺點

  • 單元測試的學習成本比較高
  • 編寫單元測試會增長程序員工做量
  • 推廣和運用單元測試須要比較大的投入

前端框架的種類

mocha, jasmine, ava, testcafe, jest前端

爲什麼選用jest

  • 方便的異步測試
  • snapshot功能
  • 集成斷言庫,不準要引用其餘第三方庫
  • 對React天生支持
    image

jest

快速開發

首先在項目中安裝 npm install --save-dev jest

添加被測試文件

// 被測試文件 sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;

書寫測試文件

// 測試文件 sum.test.js
const sum = require(‘./sum');
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

添加測試命令在package.json(固然若是是全局安裝能夠直接在項目目錄中執行 jest)

// 添加測試命令
{
  "scripts": {
    "test": "jest"
  }
}

控制檯執行npm test

內建豐富的斷言庫(列出的只是一些經常使用的)

.toBe(value)
.toHaveBeenCalled()
.toBeFalsy()
.toEqual(value)
.toBeGreaterThan(number)
.toBeGreaterThanOrEqual(number)

方便的鉤子

  • beforeEach(fn)
  • afterEach(fn)
  • beforeAll(fn)
  • afterAll(fn)

舉例,被測試文件:react

export default class Hook {

    constructor() {
        this.init();
    }

    init() {
        this.a = 1;
        this.b = 1;
    }

    sum() {
        return this.a  + this.b;
    }
}

測試用例git

import Hook from '../src/hook';


describe('hook', () => {

    const hook = new Hook;

    // 每一個測試用例執行前都會還原數據,因此下面兩個測試能夠經過。
    beforeEach( () => {
        hook.init();
    })


    test('test hook 1', () => {
        hook.a = 2;
        hook.b = 2;
        expect(hook.sum()).toBe(4);
    })

    test('test hook 2', () => {

        expect(hook.sum()).toBe(2);// 測試經過
    })


})

其餘鉤子相似能夠看文檔程序員

mock(兩種方式)

image

一. jest.fn() 方式github

被測試代碼every.jsnpm

function every(array, predicate) {
  let index = -1
  const length = array == null ? 0 : array.length

  while (++index < length) {
    if (!predicate(array[index], index, array)) {
      return false
    }
  }
  return true
}

export default every

foreach.js編程

function foreach(arr, fn) {
    for(let i = 0, len = arr.length;  i < len; i++) {
        fn(arr[i]);
    }
}

module.exports = foreach;

測試用例 mock.test.jsjson

import foreach from '../src/foreach';
import every from '../src/every';

jest.mock('../src/sum');
import sum from '../src/sum';

describe('mock test', () => {
    it('test foreach use mock', () => {
        
        // 經過jest.fn() 生成一個mock函數
        const fn = jest.fn();

        foreach([1, 2, 3], fn);
        // 測試mock函數被調用了3次
        expect(fn.mock.calls.length).toBe(3);
       // 測試第二次調用的函數第一個參數是3
        expect(fn.mock.calls[2][0]).toBe(3);
    })

    it('test every use mock return value', () => {
        const fn = jest.fn();
        
        // 能夠設置返回值
        fn
          .mockReturnValueOnce(true)
          .mockReturnValueOnce(false);


        const res = every([1, 2, 3, 4], fn);
        expect(fn.mock.calls.length).toBe(2);
        expect(fn.mock.calls[1][1]).toBe(1);
    })

    it('test every use mock mockImplementationOnce', () =>{
       // 快速定義mock的函數體,方便測試
        const fn = jest.fn((val, index) => {
            if(index == 2) {
                return false;
            }
            return true;
        });

        const res = every([1, 2, 3, 4], fn);
        expect(fn.mock.calls.length).toBe(3);
        expect(fn.mock.calls[1][1]).toBe(1);
    })

    it('test mockImplementation', () => {
       // mock函數返回值
        sum.mockImplementation(() => 15);

        expect(sum(1, 2)).toBe(15);
    })
})

二. 手動mock
假如個人測試文件sum2.jspromise

function sum2(a, b) {
    if (a > 10) return a * b;
    return a + b;
}


export default sum2;

如今若是咱們要mock sum2.js 文件的話,須要在sum2.js 同級目錄下新建文件夾__mock__,
而後在此文件下新建文件同名 sum2.js, 只是單純的返回100緩存

export default function sum2(a, b) {
    return 100;
}

測試用例mock_file.test.js

jest.mock('../src/sum2');
import sum2 from '../src/sum2';


it('test mock sum2', () => {
    // 由於此時訪問的是__mock__文件夾下的sum2.js 因此測試經過
    expect(sum2(1, 11111)).toBe(100);
})

手動mock的好處是測試和模擬分離。能夠很方便的修改測試用例。若是是複雜的mock建議使用手動新建文件方式

異步測試, 支持三種方式

  • done函數
  • return promise
  • async/await

直接上測試文件,由於superagent庫支持 promise和async/await方式,因此用superagent舉例了.
文檔地址

import superagent from 'superagent';


const target = 'http://www.baidu.com';

describe('test promise async', () => {

    it('test done', done => {
        superagent.get(target).end((err, res) => {
            expect(res).toBeTruthy();
            done();
        });
    });

    it('test promise', () => {
        return superagent.get(target).then((res) => {
            expect(res).toBeTruthy();
        });
    });

    it('test async/await', async () => {
        const res = await superagent.get(target);
        expect(res).toBeTruthy();
    });
});

Snapshot Testing

快照測試第一次運行的時候會將被測試ui組件在不一樣狀況下的渲染結果保存一份快照文件。後面每次再運行快照測試時,都會和第一次的比較。

舉例拿react組件開發測試,react-comp.js

import React from 'react';

export default class RC extends React.Component {

    render() {
        return (
            <div>我是react組件 </div>
        )
    }
}

測試用例:

import React from 'react';
import renderer from 'react-test-renderer';

import RC from '../src/react-comp';

test('react-comp snapshot test', () => {
    const component = renderer.create(<RC />);
    //
    let tree = component.toJSON();
    expect(tree).toMatchSnapshot();
})

test('react-comp snapshot test2', () => {
    const component = renderer.create(<RC />);
    
    let tree = component.toJSON();
    expect(tree).toMatchSnapshot();
})

執行測試命令,會在test目錄下生成一個__snapshots__目錄,在此目錄下會與一個文件叫snapshot.test.js.snap的快照文件

// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`react-comp snapshot test 1`] = `
<div>
  我是react組件 
</div>
`;

exports[`react-comp snapshot test2 1`] = `
<div>
  我是react組件 
</div>
`;

若是被測試代碼有正常更新可使用命令jest --updateSnapshot 從新更新緩存文件。


END, 感謝閱讀。博客地址

相關文章
相關標籤/搜索