[gist]用 jest 輕鬆測試 JavaScript

from oyanglul.usjavascript

Javascript 的測試, 無論在用 jasmine 仍是 mocha,
都是很頭疼的事情. 可是自從有了 jest, 一口氣寫7個測試, 腰也不疼了, 頭也不疼了.java

只須要 3 個理由jquery

在說用 jest 測爲何好以前,咱們先來看咱們要測的一個例子.git

栗子

好比我要寫一個模塊要去取github 用戶的follower 和他全部 repo 的 follower 數量.github

那麼咱們應該有一個 User 的 Model.ajax

// user.js
var $ = require('jquery');
function User(name) {
  this.name = name;
  this.followers = 0;
}
User.prototype.fetch = function(){
  return $.ajax({
    url: 'https://api.github.com/users/' + this.name,
    method: 'get',
    dataType: 'json'
  }).then(function(data){
      this.followers = data.followers;
  }.bind(this));
};
module.exports = User;

咱們還須要一個 repo 的 model, 大同小異略去json

最後, 整合這倆我要的東西, 並顯示在頁面上api

// follower.js
var $ = require('jquery');
function followerOf(user, repo) {
  user.fetch().then(repo.fetch).then(function(_){
    $('#content').text(user.name +"'s followers: " + user.followers +
                       " and his repo "+ repo.name +"'s followers:" + repo.followers);
    });
};

module.exports = followerOf;

--promise

1. Auto Mock

自動 mock 實在是最大的亮點, jest 重寫了 require, 因此你的代碼裏的全部 require 來的東西都自動 mock.dom

由於在你的測試中每每只關心一個模塊, 對於他的全部依賴其實都是無所謂的.

在例子中, 若是咱們在測 repo.js 的時候徹底不關心那兩個 jquery 的 ajax 方法到底
寫對沒寫對,反正咱們指望能從 ajax 裏面拿到咱們想要的東西就對了. 所以, 我但願 jquery 的
全部方法都是 mock 的. jest 讓你很輕鬆的作到這點, 由於是自動mock全部require 的東西, 而
對於目標測試模塊, 只須要說我dontMock 個人目標模塊就行了.

jest.dontMock('../repo');
describe('Repo Model', function(){
  var repo;
  beforeEach(function(){
        var $ = require('jquery').setAjaxReturn({stargazers_count: 23});
        var Repo = require('../repo');
    repo = new Repo('jcouyang', 'gira');

    });

  it('should populate properties with data from github api', function(){
        repo.fetch();
        expect(repo.followers).toBe(23);
  });
});

因此這個測試看起來就跟文檔同樣了,

  1. dontMock('./repo') 說明我關心repo
    這個模塊, 其餘我都不 care.
  2. before 是我要進行操做所須要的東西.

    • 我要 jquery ajax 請求給我想要的數據
    • 我要一個我要測的 Repo 類的實例
  3. it 說明我關心地行爲是神馬

    • 我關心 fetch 的行爲,是去取數據並給我把數據填充到個人 repo 實例中

你可能要問 segAjaxReturn 是哪裏冒出來的. 忍一忍稍後告訴你.

有沒有看雖然我顯式的 mock jquery, 可是 Repo 裏面 require 的 jquery 實際上是假的, 否則咱們就真的訪問
github api 了. 那樣就不會每次都返回 23 個 follower 了.

2. jsdom

好了如今咱們來測 follower.js, 先看 follower 到底幹了什麼, 拿到 user 和 repo
的信息而後組成一句話放到頁面 id 爲 content 的元素下面.

好, 因此咱們關心
- 組出來的話對不對
- 有沒有放到 content 元素下, 因此 jquery 的操做對不對也是咱們關心的一部分

咱們不關心
- user 幹了什麼
- repo 幹了什麼

這樣,關心的就是不能 mock 的

jest.dontMock('../follower')
    .dontMock('jquery');
describe('follower', function(){
  var user, repo, follower;
    var $ = require('jquery');
  beforeEach(function(){
        var Repo = require('../repo');
        var User = require('../user');
        follower = require('../follower');
        user = new User('jcouyang');
    repo = new Repo('jcouyang', 'gira');
    // 咱們不關心 user, 可是咱們但願他能返回一個 deferred 類型
      user.fetch.mockReturnValue($.Deferred().resolve('dont care'));
    // 咱們讓咱們不關心的 user 和 repo 返回咱們指望的東西就好
        user.name ='jcouyang';
        user.followers = 20;
        repo.name = 'gira';
        repo.followers = 21;
    // 期待頁面上有一個  id 爲 content 的元素
        document.body.innerHTML = '

<div id="content"></div>

';
    });

  it('should populate properties with data from github api', function(){
        follower(user,repo);
    // 但願 content 上能獲得想要的內容
        expect($("#content").text()).toBe('jcouyang\'s followers: 20 and his repo gira\'s followers:21');
  });
});

3. Manual Mock

好了, 說好的解釋 setAjaxReturn是怎麼回事的

嗯嗯, 是這樣的, 雖然 jest 自動 mock 了咱們不關心的模塊, 可是咱們仍是會但願
這個 mock 的玩意能有一些咱們指望的行爲, 也就是按咱們的指望返回一些東西. 好比
這裏就是咱們不關心 ajax 的邏輯, 可是咱們須要他能給咱們返回一個東西,而且能夠
thenable. 因此單純的 mock 對象或函數都不能作到, 因此有了 manual mock 這種東西.

用 manual mock 須要建一個__ mocks__ 文件夾,而後把全部的 mock 都扔進去. 好比
我想 mock jquery, 那麼我建一個jquery.js 扔進去

var data = {};
var mockDefered = function(data){
    return {
        then: function(cb){
            return mockDefered(cb(data));
        }
    };
};

function ajax() {
  return mockDefered(data);
}

function setAjaxReturn(shouldbe){
    data = shouldbe;
}
exports.setAjaxReturn = setAjaxReturn;
exports.ajax = ajax;

終於看見setAjaxReturn在哪裏定義了:sweat_smile: 這裏暴露兩個函數
- setAjaxReturn: 能夠設置我但願 ajax 返回的值
- ajax: 單純的返回這個 thenable.

因此我也不須要顯示的聲明 mock jquery什麼什麼的, 直接在測試裏設置ajax 的返回值就行了.

var $ = require('jquery').setAjaxReturn({stargazers_count: 23});

這是 repo 裏面 require 的 jquery 已經被 mock 而且只要掉 ajax 都會返回我
指望的值.

etc

  • 並行測試:
    還用說麼, 既然已經如此模塊化好了, user repo 以及 follower 的測試徹底是互不依賴.
    沒有什麼理由一個一個測. 所以3個測試的耗時取決於最長時間的那個. 因此若是有
    那個測試特別耗時,說明模塊還不夠細, 多拆幾個就快了.
  • promise: 使用 pit() 來測試 thenable 的對象, 好比 repo 的例子,就 keyi
    寫成
pit('should populate properties with data from github api', function(){
  return repo.fetch().then(
    expect(repo.followers).toBe(23);
  );
});
  • Timer mocks: 可使用 mock 的 timer 和 ticks, 也就是你能夠加速
    全部的setTimeout, setInterval, clearTimeout, clearInterval行爲. 不須要等待.
setTimeout(function() { callback(); }, 1000);
 expect(callback).not.toBeCalled();
 jest.runAllTimers();
expect(callback).toBeCalled()

Wrapup

因此說白了, jest 其實也是個概念, 推薦使用模塊化的思想, 這樣我只須要保證每一個接口的 IO 正確, 就能夠保證整個程序沒問題. 這樣劃分下來測試就會變得簡單到只須要關心固然模塊的 IO 從而 能夠 mock 掉全部其餘依賴. 真正模塊化好的代碼單純的只用 jasmine 或者 mocha 都應該是很好測的. 只是在這個概念之上省去了不少沒必要要的 mock 代碼, 由於要 mock 的 依賴老是佔大多數的, 而關心的, 每每只是那麼一兩個.

相關文章
相關標籤/搜索