我看到了两种方法:
-
spy $.ajax() 并调用返回您自己的延迟的假函数
相反:你间接测试了库
-
模拟$.RestClients 接口并返回您自己的延迟
相反:不仅需要测试回调,还需要做更多工作来模拟库。 (你的模拟越复杂,你的测试就越容易出错。)
TL;DR如果已知,请跳过此内容。
但首先让我们看看 RestClient 是如何工作的……它有两个基本对象,一个 Resource 和一个 Verb。 RestClient 实际上是一个Resource 对象(1)。 Resource 对象将返回另一个 Resource 对象,而 add()ing 一个休息片段 (2)。预定义动词read 将返回Verb 实例的call 方法(3)。
- https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L382
- https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L241
- https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L245
从该链的底部到顶部,可以从 call() 方法 (4) 访问 request 方法。如果没有显式覆盖,则默认为$.ajax()。 (5)
- https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L174
- https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L66
如果没有进行不同的配置,对read() 的调用将导致对$.ajax() 的调用,并返回一个承诺。
所以,当你使用新的new $.RestClient().add("...").add("...").read() 时,你会得到与$.ajax() 相同的效果。
变体 1:
describe("getData()", function(){
// Handle to ajax()' deferred, scoped to the
// describe so the fake ajax() and the it()
// have access to it
var def,
underTest;
beforeEach(function(){
// Mock $.ajax (or what a Verb calls in the end)
// assign "def" with a deferred and return it,
// the test can then resolve() or reject() it
spyOn($, "ajax").and.callFake(function(opts) {
def = $.Deferred();
return def;
});
// This is under test
underTest = new UnderTest();
});
afterEach(function(){
// Ensure a missing call of ajax() will fail the test
def = null;
});
it("should call callback on successful read", function() {
var callback = jasmine.createSpy("callback");
// Indirectly call ajax() which will create def
underTest.getData(callback);
// Resolve the deferred to succeed the response
def.resolve({a: 1});
expect(callback).toHaveBeenCalledWith({a: 1});
});
it("should not call callback on failed read", function(){
var callback = jasmine.createSpy("callback");
underTest.getData(callback);
def.reject();
expect(callback).not.toHaveBeenCalled();
});
});
fake 正在返回一个 deferred,而不是一个 promise,但在这种情况下它是可以的,因为它具有相同的接口,除了我们之外,没有人/没有人应该拒绝或解决 deferred。
变体 2:
describe("getData()", function(){
// Store original reference
var origRestClient,
// See first code block
def,
underTest;
// Mock thr Resouce object
function MockResource() { }
// Simplify the behaviour of this mock,
// return another Mock resource
MockResource.prototype.add = function() {
return new MockResource();
};
// What Verb.call() would do, but directly
// return a deferred
MockResource.prototype.read = function() {
def = $.Deferred();
return def;
};
beforeEach(function(){
// Replace RestClient
origRestClient = $.RestClient;
$.RestClient = MockResource;
underTest = new UnderTest();
});
afterEach(function(){
// Restore RestClient
$.RestClient = origRestClient;
def = null;
});
it("should call callback on successful read", function() {
var callback = jasmine.createSpy("callback");
underTest.getData(callback);
def.resolve({a: 1});
expect(callback).toHaveBeenCalledWith({a: 1});
});
it("should not call callback on failed read", function(){
var callback = jasmine.createSpy("callback");
underTest.getData(callback);
def.reject();
expect(callback).not.toHaveBeenCalled();
});
});
如果您还想测试路径和请求数据,Resouce 的模拟需要比我做的更多的工作,使用上面的代码这是不可能的。