跟后端代码不太一样的东西无非就是
AJAX
因此男厕的也就是他们
it('should hide specific column by display filter', function () {
$option1 = $('.ui-multiselect-checkboxes input[type=checkbox]:eq(1)');
$option1.attr("checked", false).trigger("click");
expect($($table.find('td:eq(2)'))).toBeHidden();
});
它测的是这个模块...
ns("REA.reports.dashboard.TableFilter", function (ns) {
ns.initialize = function (filter) {
var $filter = $(filter);
var $table = $($filter.data("table"));
var checkboxes = $filter.multiselect("widget").find(":checkbox");
$.each(checkboxes, function () {
var $this = $(this);
var idx = parseInt(this.id.split('-').pop(), 10) + 2;
var $th = $table.find('th:nth-child(' + idx + ')');
var $column = $table.find('td:nth-child(' + idx + ')');
if ($this.is(":checked")) {
$th.show();
$column.show();
$('.display-filters:visible select option[value="' + $this.val() + '"]').attr({selected: 'selected'});
} else {
$th.hide();
$column.hide();
$('.display-filters:visible select option[value="' + $this.val() + '"]').removeAttr('selected');
}
});
REA.reports.dashboard.SlideNav.toggleSlideButton($table);
};
});
$option1.attr("checked", false).trigger("click")
这是几个意思单元测试 -> 输入输出
UI test -> 操作, 行为
$option1.attr("checked", false).trigger("click")
ns("REA.reports.dashboard.TableFilter", function (ns) {
ns.initialize = function (filter) {
var $filter = $(filter); // 依赖注入, mock 之
var $table = $($filter.data("table")); // 返回的 data 也要 mock
var checkboxes = $filter.multiselect("widget").find(":checkbox"); //不关心 multiselect, mock 之
$.each(checkboxes, function () {
var $this = $(this);
var idx = parseInt(this.id.split('-').pop(), 10) + 2; // 逻辑
var $th = $table.find('th:nth-child(' + idx + ')'); // 逻辑
var $column = $table.find('td:nth-child(' + idx + ')');
if ($this.is(":checked")) {
$th.show(); //逻辑
$column.show(); //逻辑
$('.display-filters:visible select option[value="' + $this.val() + '"]').attr({selected: 'selected'});
} else {
$th.hide(); //逻辑
$column.hide(); //逻辑
$('.display-filters:visible select option[value="' + $this.val() + '"]').removeAttr('selected'); //逻辑
}
});
REA.reports.dashboard.SlideNav.toggleSlideButton($table); //逻辑
};
});
+-------------------+
input | | output
---------------+ function +--------------
| |
| |
+-------------------+
当然是单输入输出
来写一个模块要去取github 用户的 follower 数量的模块.
那么我们应该有一个 User 的 Model.
// 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;
这段代码写得应该算非常好测的,没有输入,输出是fetch之后populate User model
所以只要
$.ajax
传入争取的地址$.ajax
后把数据(follower)放入model中注意到依赖 jquery 的 ajax 方法
jquery是第三方库,我们并不关心他对不对,因为知道给定正确输入肯定能得到确定的结果(不然我也不用它)。应该mock它,但是怎么...
好像不好下手啊
User.prototype.fetch = function(ajax){
return ajax({
...
})
...
};
非常好测,把mock的ajax函数传进去即可
每次使用fetch的时候还要这样写
new User('jcouyang').fetch($.ajax)
为了测试的方便, 牺牲了使用接口的易用性
jest.dontMock('../user');
describe('User Model', function(){
var user;
beforeEach(function(){
var $ = require('jquery').setAjaxReturn({followers: 23});
var User = require('../user');
user = new User('jcouyang');
});
it('should populate properties with data from github api', function(){
user.fetch();
expect(user.followers).toBe(23);
});
});
所以这个测试看起来就跟文档一样了,
beforeEach(function(){
var $ = require('jquery').setAjaxReturn({followers: 23});
var User = require('../user');
user = new User('jcouyang');
});
忍一忍稍后告诉你.
有没有看虽然我没有显式的 mock jquery, 但是 User 里面 require 到的 jquery 其实是假的, 不然我们就真的访问 github api 了. 那样就不会每次都返回 23 个 follower 了.
好了现在我们来测 follower.js, 先看 follower 到底干了什么, 拿到 user 的信息然后组成一句话放到页面 id 为 content 的元素下面.
好, 所以我们关心
我们不关心
这样,关心的就是不能 mock 的
jest.dontMock('../follower')
.dontMock('jquery');
describe('follower', function(){
var user, repo, follower;
var $ = require('jquery');
beforeEach(function(){
var User = require('../user');
follower = require('../follower');
user = new User('jcouyang');
// 我们不关心 user, 但是我们希望他能返回一个 deferred 类型
user.fetch.mockReturnValue($.Deferred().resolve('dont care'));
// 我们让我们不关心的 user 返回我们期望的东西就好
user.name ='jcouyang';
user.followers = 20;
// 期待页面上有一个 id 为 content 的元素
document.body.innerHTML = '<div id="content"></div>';
});
it('should populate properties with data from github api', function(){
follower(user);
// 希望 content 上能得到想要的内容
expect($("#content").text()).toBe('jcouyang\'s followers: 20');
});
});
好了, 说好的解释 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: 这里暴露两个函数
所以我也不需要显示的声明 mock jquery什么什么的, 直接在测试里设置ajax 的返回值就好了.
var $ = require('jquery').setAjaxReturn({stargazers_count: 23});
这是 repo 里面 require 的 jquery 已经被 mock 并且只要掉 ajax 都会返回我 期望的值.
pit('should populate properties with data from github api', function(){
return repo.fetch().then(
expect(repo.followers).toBe(23);
);
});
setTimeout(function() { callback(); }, 1000);
expect(callback).not.toBeCalled();
jest.runAllTimers();
expect(callback).toBeCalled()