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

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

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

PS:
实际上,在这两种情况下都会调用fakeFunction的done和fail Deferred Object。


(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)

 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? }); }); }); 

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

所以可能会是这样的:

 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(); }); }); 

另一件事,如果可以的话,尝试分解function,这样你就不会在一次测试中测试整个DOM-to-response-callback路径。 如果你足够精细,你可以通过在测试中使用Deferred对象来实际测试异步延迟分辨率!

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

 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' }); }); }); 

以下是我设法做到的方法。

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

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

测试

 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(); }); 

如果你有一个带有ajax请求promise对象的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(); }); }); 

下面我发布可能适合您的未经测试的代码。 我想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: $('
' + '' + '' + '
') }); 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. }); }); });

但是,我不确定这条线

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

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

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