2012-02-02 10 views
20

Estoy tratando de probar un clic del botón usando backbone.js, jasmine.js y sinon.js. Pero el siguiente caso de prueba falla. Estoy usando un espía para rastrear si se llama o no. ¿Puedes ayudarme con esto?backbone.js haga clic en el evento espía no se llama usando jasmine.js y sinon.js

Gracias.

tarea nueva plantilla

<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 caso de prueba

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 salida

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

¿En qué el DOM en el render en sí? ¿La plantilla está disponible dentro de las especificaciones? Probablemente el botón # add_new_task no existe en el DOM del corredor de especificaciones Jasmine y, por lo tanto, '$ (" # add_new_task "). Click();' no tiene ningún efecto. Si está seguro de que la vista se representa con la plantilla correcta, podría usar el elemento NewTaskView como contexto para la función jquery: '$ ('# add_new_task', this.view.el) .click();'. –

+0

Creo que esta pregunta ya está respondida aquí: http://stackoverflow.com/questions/8441612/why-is-this-sinon-spy-not-being-called-when-i-run-this-test/9012788# 9012788 – vadimich

Respuesta

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

significa que va a enlazar el evento click a this.el - elemento raíz de la opinión de que en su caso tiene un ID new_task_section. Necesitas vincularlo al #add_new_task, que es el botón de "agregar tarea", supongo. ¡Esto debería arreglarlo!

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

Actualización:

$ ("# add_new_task") no va a encontrar el elemento que la vista no se añade a la estructura del documento DOM. use this.view.$('#add_new_task') y debería funcionar ya que buscará el elemento en el fragmento separado almacenado en la vista.

+0

En realidad también tuve este "cliC#add_new_task" y el evento se desencadena ya que tengo la línea console.log en el método addTask. pero espera (espiar) .toHaveBeenCalled() falla; ¿Puede ser que mi enlace sea incorrecto? – felix

+0

ah, ya lo sé, creo - agregado en una edición –

+0

si #add_new_task no está en esta vista, ¿no está siendo encuadernado en el lugar equivocado? – Tjorriemorrie

0

Hay algunos problemas con su enfoque. Primero espiar en su clase que desea probar, con no es la forma en que una prueba de unidad debería funcionar porque prueba la lógica interna de su clase y no su comportamiento. En segundo lugar, y esa es la razón por la cual su prueba falla, no ha adjuntado sus puntos de vista al DOM. Entonces, puede agregar su el al DOM o disparar el evento click directamente al el: $('#add_new_task', this.view.el).click().

Btw. La forma de backbones de crear elementos y unir eventos hace que sea difícil escribir buenas pruebas unitarias porque te obligan a trabajar con DOM y jquery. Una mejor forma de escribir código comprobable sería pasar siempre todas las dependencias al constructor y no crear nuevas instancias en el código porque es difícil probar estos objetos. Entonces, en su caso, sería mucho más fácil inyectar el objeto el en el constructor como un objeto jquery y en los eventos de forma manual. Haciéndolo de esta manera puedes probar tu clase sin ninguna dependencia al DOM o jquery.

Así que en su caso el constructor se vería así:

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

Y su prueba:

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

Después de todo lo que tiene que decidir qué enfoque se ajuste a sus necesidades. Código más simple con ayudantes de backbones o código mejor comprobable con menos dependencias para jquery, DOM y backbone.

+0

Jazmín no es un marco de pruebas unitarias - es el marco BDD y lo que hace está perfectamente bien - probando un comportamiento –

+0

No estoy seguro cuál es su punto. Espiar la clase que quieres probar no tiene mucho sentido. Porque no es el comportamiento de su clase llamar a una función específica en sí mismo. Cambiar el nombre de esta función romperá la prueba, pero no el comportamiento. El otro pensamiento es que creo que es mejor burlarse de tantas dependencias como puedas. Usar la inyección de dependencia hace que sea mucho más fácil porque no tienes que cargar un dispositivo antes de la prueba, disparar un evento de clic y limpiarlo después. –

41

El problema es que agrega su espía después de que la red troncal ya ha vinculado el evento click directamente a la función addTask (lo hace durante la construcción de la Vista). Por lo tanto, tu espía no será llamado.

Intente conectar el espía a un prototipo de la Vista antes de lo construye. De esta manera:

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

y luego recordar para eliminarlo:

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

Esta respuesta debe ser aceptada. ¡Gracias, esto fue un dolor de cabeza! – Backus

+0

¡Tiene total sentido! ¡Gracias! :) – nuc

+2

lo que no está claro para mí es: ¿por qué necesito espiar el prototipo si quiero espiar solo una instancia del prototipo? ¿Es eso imposible? – dierre

Cuestiones relacionadas