2012-08-22 14 views
28

Ecco il codice relativo alla richiesta di invio javascript (1).
Ecco il test per deridere la richiesta Ajax utilizzando jasmine (2).Come testare il Deferred Object fatto e fallito usando jasmine

Mi piacerebbe prendere in giro il comportamento del server. Qualche idea?
Vedere il commento in (1) e (2) per ulteriori dettagli.

P.S .:
In realtà, in entrambi i casi vengono chiamati l'Oggetto rinviato di FAke di FakeFunction.


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

}); 
+0

Utilizzando un wrapper per il tuo chiamate AJAX renderà testare loro molto più facile. Vedi https://github.com/webadvanced/takeCommand – Paul

+1

@Paul grazie per il tuo suggerimento. In realtà non capisco esattamente perché dovrei usare '/ webadvanced/takeCommand' dato che' $ .ajax' ha già ciò di cui ho bisogno. Potresti rispondermi scrivendo un codice di esempio? –

+0

var spy = spyOn (class, "metodo"); actionThatCallsClassMethod(); expect (spy) .toHaveBeenCalled(); mi ha aiutato nel tuo esempio. – Aligned

risposta

24

Abbiamo effettivamente incontrato lo stesso problema, cercando di verificare gli oggetti che rappresentano differite ajaxed modelli di script per template on-the-fly. La nostra soluzione di test prevede l'utilizzo della libreria Jasmine-Ajax insieme a Jasmine.

Quindi probabilmente sarà qualcosa di simile:

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

Un'altra cosa, se è possibile, cercare di rompere la funzionalità in modo da non sta testando l'intero percorso DOM-to-risposta-richiamata nei un test. Se sei abbastanza granulare, puoi effettivamente testare risoluzioni differite asincrone usando oggetti Deferred stessi all'interno dei tuoi test!

La chiave è effettivamente utilizzare gli oggetti rinviati all'interno dei test stessi, in modo che l'ambito della chiamata expect sia ancora all'interno del blocco funzione 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' }); 
    }); 
}); 
+0

Questo è sicuramente l'approccio da adottare - È bello sapere che il gelsomino ha questo built-in, ma se si sta utilizzando qunit o altri framework di test delle unità - [sinon.js] (http://sinonjs.org/) ha tutti questi mozziconi/falsiXHR ecc. – gnarf

+0

Si noti che come per 'jasmine-ajax 3.0' response' viene sostituito con 'replyWith', si veda [note di rilascio] (https://github.com/jasmine/jasmine-ajax/blob/master/release_notes/3.0.md) – ivarni

3

Sarebbe molto più facile per verificare se si ha un var con l'oggetto di richiesta Ajax promessa. In tal caso si potrebbe fare:

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

Di seguito inserisco codice non verificato che potrebbe essere adatto a te. Suppongo che la chiamata ajax sia inizializzata dalla classe .submit()? Forse dovresti inizializzare la richiesta Ajax da un blocco runs() e non da beforeEach(), ma dovresti provare quale funziona.

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

    }); 
}); 

Ma, io non sono sicuro che la linea

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

fa ciò che si vuole. È possibile spiare un'altra spia? E se sì, perché chiami la spia? Forse si dovrebbe cercare

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

Ecco come sono riuscito a farlo.

In sostanza, l'oggetto $ .ajax restituisce un oggetto rinviato, quindi è possibile spiare $ .ajax e restituire un rinviato e quindi attivarlo manualmente per eseguire il.fatto di codice() nella tua JavaScript

Codice

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

prova

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

PS per spiare il fallimento, invece di chiamare deferred.resolve() chiama deferred.reject() – Tony

+0

molto semplice, grazie! – dbrin

Problemi correlati