javascript 如何使用 jasmine 测试已完成和失败的延迟对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12080087/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 15:16:22  来源:igfitidea点击:

How to test the done and fail Deferred Object by using jasmine

javascriptjquerypostjasmine

提问by Lorraine Bernard

Here is the code about the javascript submit request (1).
Here is the test about mocking the ajax request by using jasmine (2).

这是关于javascript提交请求的代码(1)。
下面是使用jasmine模拟ajax请求的测试(二)。

I would like to mock the server behaviour. Any ideas?
See the comment in (1) and (2) for more details.

我想模拟服务器行为。有任何想法吗?
有关更多详细信息,请参阅 (1) 和 (2) 中的注释。

P.S.:
Actually in both case the done and the fail Deferred Object of fakeFunction are called.

PS:
实际上在这两种情况下,都调用了 fakeFunction 的 done 和 fail 延迟对象。



(1)

(1)

submitForm: function () {
     // the server execute fail only if message.val() is empty
     // and I would like to mock this behaviour in (2)
     backendController.submitForm(message.val()).done(this.onSuccess).fail(this.onError);
},

backendController.submitForm = function (message) {
    return $.ajax({
        url: 'some url',
        type: 'POST',
        dataType: 'json',
        data: {
            message: message
        }
    }).done(function () {
        //some code;
    });
};


(2)

(2)

describe('When Submit button handler fired', function () {
    var submitFormSpy,
        fakeFunction = function () {
            this.done = function () {
                return this;
            };
            this.fail = function () {
                return this;
            };
            return this;
        };

    beforeEach(function () {
        submitFormSpy = spyOn(backendController, 'submitForm').andCallFake(fakeFunction);
    });

    describe('if the message is empty', function () {
        beforeEach(function () {
            this.view.$el.find('#message').text('');
            this.view.$el.find('form').submit();
        });
        it('backendController.submitForm and fail Deferred Object should be called', function () {
            expect(submitFormSpy).toHaveBeenCalled();
            // how should I test that fail Deferred Object is called?
        });
    });

    describe('if the message is not empty', function () {
        beforeEach(function () {
            this.view.$el.find('#message').text('some text');
            this.view.$el.find('form').submit();
        });
        it('backendController.submitForm should be called and the fail Deferred Object should be not called', function () {
            expect(submitFormSpy).toHaveBeenCalled();
            // how should I test that fail Deferred Object is not called?
        });
    });

});

采纳答案by Aintaer

We actually ran into the same problem, trying to test Deferred objects that represent AJAXed template scripts for on-the-fly templating. Our testing solution involves using the Jasmine-Ajaxlibrary in conjunction with Jasmine itself.

我们实际上遇到了同样的问题,试图测试代表 AJAX 模板脚本的延迟对象,以进行动态模板化。我们的测试解决方案涉及将Jasmine-Ajax库与 Jasmine 本身结合使用。

So probably it will be something like this:

所以可能它会是这样的:

describe('When Submit button handler fired', function () {
  jasmine.Ajax.useMock();

  describe('if the message is empty', function () {

    beforeEach(function() {
      spyOn(backendController, 'submitForm').andCallThrough();
      // replace with wherever your callbacks are defined
      spyOn(this, 'onSuccess');
      spyOn(this, 'onFailure');
      this.view.$el.find('#message').text('');
      this.view.$el.find('form').submit();
    });

    it('backendController.submitForm and fail Deferred Object should be called', function () {
      expect(backendController.submitForm).toHaveBeenCalledWith('');
      mostRecentAjaxRequest().response({
        status: 500, // or whatever response code you want
        responseText: ''
      });

      expect( this.onSuccess ).not.toHaveBeenCalled();
      expect( this.onFailure ).toHaveBeenCalled();
    });
});

Another thing, if you can, try to break up the functionality so you're not testing the entire DOM-to-response-callback path in one test. If you're granular enough, you can actually test asynchronous Deferred resolutions by using Deferred objects themselves inside your tests!

另一件事,如果可以的话,尝试分解功能,这样您就不会在一次测试中测试整个 DOM 到响应回调路径。如果您足够细化,您实际上可以通过在测试中使用延迟对象本身来测试异步延迟解决方案!

The key is to actually use Deferred objects within your tests themselves, so that the scope of the expectcall is still within your itfunction block.

关键是在你的测试中实际使用 Deferred 对象,这样expect调用的范围仍然在你的it功能块内。

describe('loadTemplate', function() {
  it('passes back the response text', function() {
    jasmine.Ajax.mock();
    loadTemplate('template-request').done(function(response) {
      expect(response).toBe('foobar');
    });
    mostRecentAjaxRequest().response({ status:200, responseText:'foobar' });
  });
});

回答by Tony

Here is how I managed to do it.

这是我设法做到的。

Essentially, the $.ajax object returns a Deferred object, so you can spy on $.ajax and return a Deferred and then trigger it manually to run the .done() code in your JavaScript

本质上,$.ajax 对象返回一个 Deferred 对象,因此您可以监视 $.ajax 并返回一个 Deferred,然后手动触发它以在您的 JavaScript 中运行 .done() 代码

Code

代码

Index.prototype.sendPositions = function() {
  var me = this;
  $.ajax({
    ...
  }).done(function(data, textStatus, jqXHR) {
    me.reload();
  }).fail(function(jqXHR, textStatus, errorThrown) {
    console.log(errorThrown);
  });
};

Test

测试

it("should reload the page after a successful ajax call", function(){
  var deferred = new jQuery.Deferred();
  spyOn($, 'ajax').andReturn(deferred);
  spyOn(indexPage, 'reload');
  indexPage.sendPositions();
  deferred.resolve('test');
  expect(indexPage.reload).toHaveBeenCalled();
});

回答by basos

It would be much easier to test if you had a var with the ajax request promise object. In that case you could do:

如果您有一个带有 ajax 请求承诺对象的 var ,测试会容易得多。在这种情况下,你可以这样做:

 it('should do an async thing', function() {     
   var mutex = 1;
   var promF = jasmine.createSpy('prF');

   runs( function() {
     var promise1 = $.ajax();
     promise1.always(function(){
       mutex--;
     });
     promise1.fail(function(){
       promF();
     });
   });

   waitsFor(function(){
     return !mutex;
   }, 'Fetch should end', 10000);

   runs( function() {
      expect(promF).toHaveBeenCalled();
   });
 });

Below I post untested code that might suit you. I suppose that the ajax call is initialized from the .submit() class? Maybe you should initialize the ajax request from a runs() block and not from beforeEach(), but you should try which one works.

下面我发布了可能适合您的未经测试的代码。我想 ajax 调用是从 .submit() 类初始化的?也许您应该从runs() 块而不是从beforeEach() 初始化ajax 请求,但是您应该尝试哪个有效。

describe('When Submit button handler fired and city is defined', function () {
    var ajaxRequestSpy,
        failSpy, successSpy, alwaysSpy,
        mutex;
    beforeEach(function () {
        ajaxRequestSpy = spyOn(backendController, 'ajaxRequest').andCallThrough();
        failSpy = spyOn(ajaxRequestSpy(), 'fail').andCallThrough()
        successSpy = spyOn(ajaxRequestSpy(), 'success').andCallThrough();
        mutex = 1; // num of expected ajax queries
        alwaysSpy =  spyOn(ajaxRequestSpy(), 'always').andCallFake(function() {
             mutex--;
        });
        this.view = new MyView({
            el: $('<div><form>' +
                '<input type="submit" value="Submit" />' +
                '<input type="text" name="city">' +
                '</form></div>')
        });
        this.view.$el.find('form').submit();
    });
    it('backendController.ajaxRequest should be called', function () {
        runs( function() {
            // maybe init ajax here ?   
        });

        waitsFor( function() {
            return !mutex;
        }, 'ajax request should happen', 5000);

        runs( function() {
            expect(ajaxRequestSpy).toHaveBeenCalled(); // true
            expect(failSpy).toHaveBeenCalled(); // Error: Expected spy fail 
                                            // to have been called.
        });

    });
});

But, I am not sure that the line

但是,我不确定这条线

failSpy = spyOn(ajaxRequestSpy(), 'fail').andCallThrough();

does what you want. Is it possible to spy on another spy? And if yes why are you calling the spy ? Maybe you should try

做你想做的。是否有可能监视另一个间谍?如果是的话,你为什么要打电话给间谍?也许你应该试试

failSpy = spyOn(ajaxRequestSpy, 'fail').andCallThrough();