模拟嵌套函数返回在Jasmine中延迟

时间:2015-03-25 21:10:30

标签: javascript jquery unit-testing jasmine

我正在尝试为使用名为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 个答案:

答案 0 :(得分:1)

我认为有两种方法可以做到这一点:

  1. spy $.ajax()并调用返回您自己的延迟

    的假函数

    Contra:你间接测试了库

  2. 模拟$.RestClient界面并返回您自己的延迟

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

  3. TL; DR 如果已知,请跳过此。

    但首先让我们看一下RestClient的工作原理......它有两个基本对象,一个是资源和一个动词。 RestClient实际上是一个Resource对象(1)。当Resource休息片段(2)时,Resource个对象将返回另一个add()对象。预定义的动词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
    4. 从该链的底部到顶部,可以从request方法(4)访问call()方法。如果未明确覆盖,则默认为$.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
      3. 如果配置不同,拨打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();
            });
        });
        

        假货正在退货,而不是承诺,但在这种情况下它没有问题,因为它有相同的界面,除了我们之外,没有人/没有人应该拒绝或解决延期。

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