[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 的 依赖老是占大多数的, 而关心的, 每每只是那么一两个.

相关文章
相关标签/搜索