2012-02-02 8 views
20

Sto provando a fare clic su un pulsante con il comando backbone.js, jasmine.js e sinon.js. Ma il seguente test fallisce. Sto usando una spia per controllare se viene chiamato o meno. Puoi aiutarmi per favore con questo?backbone.js click spia evento non viene richiamato utilizzando jasmine.js e sinon.js

Grazie.

Nuova operazione Template

<script id='new_task_template' type='text/template'> 
    <input type='text' id='new_task_name' name='new_task_name'></input> 
    <button type='button' id='add_new_task' name='add_new_task'>Add Task</button> 
</script> 

NewTaskView

T.views.NewTaskView = Backbone.View.extend({ 
    tagName: 'section', 
    id: 'new_task_section', 
    template : _.template ($("#new_task_template").html()), 
    initialize: function(){ 
    _.bindAll(this, 'render', 'addTask'); 
    }, 
    events:{ 
    "click #add_new_task" : "addTask" 
    }, 
    render: function(){ 
    $(this.el).html(this.template()); 
    return this; 
    }, 
    addTask: function(event){ 
    console.log("addTask"); 
    } 
}); 

Jasmine Test Case

describe("NewTaskView", function(){ 
    beforeEach(function(){  
    this.view = new T.views.NewTaskView(); 
    this.view.render(); 
    }); 

    it("should #add_new_task is clicked, it should trigger the addTask method", function(){ 
    var clickSpy = sinon.spy(this.view, 'addTask'); 
    $("#add_new_task").click(); 
    expect(clickSpy).toHaveBeenCalled(); 
    }); 
}); 

Jasmine uscita

NewTaskView 
    runEvents 
    runshould #add_new_task is clicked, it should trigger the addTask method 
     Expected Function to have been called. 
+0

Dove nel DOM fa la vista stessa render? Il modello è disponibile anche all'interno delle specifiche? Probabilmente il pulsante # add_new_task non esiste nel DOM del runner di specifiche di Jasmine e quindi '$ (" # add_new_task "). Click();' non ha alcun effetto. Se sei sicuro che la vista sia resa con il modello corretto, puoi usare l'elemento di NewTaskView come contesto per la funzione jquery: '$ ('# add_new_task', this.view.el) .click();'. –

+0

Penso che questa domanda abbia già una risposta qui: http://stackoverflow.com/questions/8441612/why-is-this-sinon-spy-not-being-called-when-i-run-this-test/9012788# 9012788 – vadimich

risposta

0
events:{ 
    "click" : "addTask" 
}, 

significa che si sta associando l'evento click per this.el - elemento principale della vista che nel tuo caso dispone di un ID new_task_section. È necessario associarlo a #add_new_task che è il pulsante "aggiungi attività" presumo - questo dovrebbe risolverlo!

events:{ 
    "click #add_new_task" : "addTask" 
}, 

Aggiornamento:

$ ("# add_new_task") non troverà l'elemento come la vista non viene aggiunto alla struttura del documento DOM. utilizzare this.view.$('#add_new_task') e dovrebbe funzionare mentre cercherà l'elemento nel frammento staccato memorizzato nella vista.

+0

In realtà avevo anche questo "click #add_new_task" e l'evento si attiva perché ho la riga console.log nel metodo addTask. ma si aspetta (spy) .toHaveBeenCalled() fallisce; Può essere che il mio legame sia sbagliato? – felix

+0

ah so che penso - aggiunto in una modifica –

+0

se #add_new_task non è in questa vista, non è che si lega nel posto sbagliato? – Tjorriemorrie

0

Ci sono alcuni problemi con il tuo approccio. Per prima cosa spiate la vostra classe che volete testare, con non è il modo in cui un test unitario dovrebbe funzionare, perché testate la logica interna della vostra classe e non il suo comportamento. In secondo luogo, e questo è il motivo per cui il test fallisce, non hai allegato le tue viste nel DOM. Quindi, puoi attaccare il tuo el al DOM o licenziare direttamente l'evento click al numero: $('#add_new_task', this.view.el).click().

Btw. il modo di backbone per creare eventi di tipo element e bind rende difficile scrivere buoni test unitari perché sei costretto a lavorare con DOM e jquery. Un modo migliore per scrivere codice testabile è quello di passare sempre tutte le dipendenze al costruttore e non creare nuove istanze nel codice, poiché è difficile testare questi oggetti. Quindi nel tuo caso sarebbe molto più facile iniettare l'oggetto el nel costruttore come un oggetto jQuery e agli eventi manualmente. Facendolo in questo modo puoi testare la tua classe senza alcuna dipendenza dal DOM o dalla jQuery.

Quindi nel tuo caso il costruttore sarebbe simile a questa:

initialize: function(){ 
    this.el.click(_.bind(this, 'addTask')); 
} 

E il test:

var el = {click: function(){}}; 
spyOn(el, 'click'); 
new T.views.NewTaskView({el: el}); 
expect(el.click).toHaveBeenCalled(); //test the click event was bind 
//call the function that was bind to the click event, 
//which is the same as trigger the event on a real DOM object 
el.click.mostRecentCall.args[0]() 

Dopo tutto si deve decidere quale approccio si adatta alle vostre esigenze. Codice più snello con helper backbone o codice testabile migliore con meno dipendenze da jquery, DOM e backbone.

+0

Jasmine non è un framework di test unitario: è il framework BDD e ciò che fa è perfettamente soddisfacente - testare un comportamento –

+0

Non sai quale sia il tuo punto. Spiare la classe che vuoi testare non ha molto senso. Perché non è il comportamento della classe a chiamare una funzione specifica su se stessa. La modifica del nome di questa funzione interromperà il test ma non il comportamento. L'altro credo sia che penso sia meglio deridere quante più dipendenze possibili. L'utilizzo dell'iniezione di dipendenza rende molto più semplice la causa del caricamento di un proiettore prima del test, l'attivazione di un evento clic e la pulizia successiva. –

41

Il problema è di aggiungere la spia dopo che il backbone ha già associato l'evento click direttamente alla funzione addTask (lo fa durante la costruzione della vista). Quindi la tua spia non verrà chiamata.

Provare ad allegare la spia a un prototipo della vista prima del lo si costruisce. Come questo:

this.addTaskSpy = sinon.spy(T.views.NewViewTask.prototype, 'addTaskSpy'); 
this.view = new T.views.NewTaskView(); 

e poi ricordare per rimuoverlo:

T.views.NewViewTask.prototype.addTaskSpy.restore() 
+4

Questa risposta dovrebbe essere accettata. Grazie, questo era un mal di testa! – Backus

+0

Ha senso! Grazie! :) – nuc

+2

quello che non mi è chiaro è: perché ho bisogno di spiare il protype se voglio spiare solo un'istanza del prototipo. È impossibile? – dierre

Problemi correlati