Backbone.js click event spy не вызывается с использованием jasmine.js и sinon.js
Я пытаюсь проверить нажатие кнопки с помощью backbone.js, jasmine.js и sinon.js. Но следующий тестовый пример терпит неудачу. Я использую шпион, чтобы отслеживать, будет ли он вызван или нет.
Не могли бы вы помочь мне с этим?
Спасибо.
Новый шаблон задачи
<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");
}
});
Тест на жасмин
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();
});
});
Выход жасмина
NewTaskView
runEvents
runshould #add_new_task is clicked, it should trigger the addTask method
Expected Function to have been called.
Ответы
Ответ 1
Проблема заключается в том, что вы добавляете своего шпиона после того, как позвоночник уже привязал событие click непосредственно к функции addTask (он делает это во время построения View). Поэтому ваш шпион не будет вызван.
Попробуйте подключить шпиона к прототипу представления перед его созданием. Вот так:
this.addTaskSpy = sinon.spy(T.views.NewViewTask.prototype, 'addTaskSpy');
this.view = new T.views.NewTaskView();
а затем не забудьте удалить его:
T.views.NewViewTask.prototype.addTaskSpy.restore()
Ответ 2
events:{
"click" : "addTask"
},
означает, что вы привязываете событие click к this.el
- корневому элементу представления, которое в вашем случае имеет идентификатор new_task_section
. Вам нужно привязать его к #add_new_task
, который является вашей кнопкой "добавить задачу", которую я предполагаю, - это должно исправить это!
events:{
"click #add_new_task" : "addTask"
},
Update:
$( "# add_new_task" ) не найдет элемент, поскольку представление не добавлено в дерево DOM документа. используйте this.view.$('#add_new_task')
, и он должен работать, поскольку он будет искать элемент в отдельном фрагменте, хранящемся в представлении.
Ответ 3
Есть некоторые проблемы с вашим подходом. Сначала вы шпионите за своим классом, который хотите испытать, потому что это не способ работать unit test, потому что вы проверяете внутреннюю логику своего класса, а не его поведение. Во-вторых, и это причина, по которой ваш тест терпит неудачу, вы не привязали свои взгляды в DOM. Таким образом, либо вы прикрепляете ваш el к DOM, либо запускаете событие click непосредственно в el: $('#add_new_task', this.view.el).click()
.
Btw. магистральный способ создания элементов и привязки событий затрудняет запись хороших модульных тестов, потому что вы вынуждены работать с DOM и jquery. Лучшим способом написания тестового кода было бы всегда передавать все зависимости в конструктор и не создавать новые экземпляры в коде, потому что его трудно проверить эти объекты. Таким образом, в вашем случае было бы намного проще вставить объект el в конструкторе как объект jquery и в событиях вручную. Таким образом, вы можете проверить свой класс без каких-либо зависимостей с DOM или jquery.
Итак, в вашем случае конструктор будет выглядеть так:
initialize: function(){
this.el.click(_.bind( this, 'addTask'));
}
И ваш тест:
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]()
В конце концов, вам нужно решить, какой подход будет соответствовать вашим потребностям. Leaner-код с помощниками магистралей или лучше тестируемый код с меньшими зависимостями от jQuery, DOM и магистрали.