Jasmine中返回Deferred的模拟嵌套函数

Mocking nested function returning Deferred in Jasmine

本文关键字:模拟 嵌套 函数 Deferred 返回 Jasmine      更新时间:2023-09-26

我正试图为一个使用名为jQuery.rest 的jQuery ajax包装库的模块编写Jasmine测试

该测试模块:

var module = function() {
  function getData(callback) {
    IP.read().done(function (data) {
        console.log("done");
        callback(data);
    });
  }
  return {
    getData: getData
  }
}();

clientIP变量在不同的文件中声明,如下所示:

var client = new $.RestClient('/rest/api/');
var IP = client.add('ip');

我想模拟read()函数,这样它就会返回我在测试中定义的Json有效负载。read()方法返回一个$.Deferred对象。

我尝试过不同的方法(使用Jasmine间谍),但没有成功。

我看到了两种方法:

  1. 监视$.ajax()并调用返回您自己的延迟的fake函数

    Contra:您间接测试库

  2. 模拟$.RestClient的接口并返回您自己的延迟

    Contra:当不仅需要测试回调时,还要做更多的工作来模拟库。(你的嘲讽越复杂,你的测试就越容易出错。)


TL;DR如果已知,则跳过此项。

但首先让我们看看RestClient是如何工作的。。。它有两个基本对象,一个资源和一个动词。RestClient实际上是一个Resource对象(1)。当CCD_ 11返回一个休息片段(2)时,CCD_ 9对象将返回另一个CCD_。预定义的谓词read将返回Verb实例的call方法(3)。

  1. https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L382
  2. https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L241
  3. https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L245

从该链的底部到顶部,可以从call()方法(4)访问request方法。如果未明确重写,则默认为$.ajax()。(5)

  1. https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L174
  2. https://github.com/jpillora/jquery.rest/blob/gh-pages/dist/jquery.rest.js#L66

如果没有不同的配置,对read()的调用将导致对$.ajax()的调用,并返回promise。

所以,当做新的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();
    });
});

假的是返回一个延迟的,而不是承诺,但在这种情况下,这是可以的,因为它有相同的接口,除了我们之外,任何人都不应该拒绝或解决延迟。

变体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的嘲讽需要比我所做的更多的工作,而使用上面的代码,这是不可能的。