spyOn метод представления магистрали с использованием jasmine

#view #backbone.js #jasmine #spy

#Вид #backbone.js #jasmine #шпионить

Вопрос:

У меня есть представление магистрали, и я хочу создать тест, чтобы подтвердить, что событие щелчка по какому-либо элементу вызовет функцию, привязанную к этому элементу. Мое мнение таково:

 PromptView = Backbone.View.extend({
        id:"promptPage",
        attributes:{
            "data-role":"page",
            "data-theme":"a"
        },
        events:{
            "click #btnYes":    "answerYes",
            "tap #btnYes":      "answerYes"
        },
        render: function(){
            $(this.el).html(_.template($('#promptPage-template').html(), this.model.toJSON()));

            return this;
        },
        answerYes: function(){
            alert('yes');
        }
    });
  

Моя спецификация:

 beforeEach(function() {
            model = new PromptModel;
            view = new PromptView({model:model});
            loadFixtures('promptPage.tmpl');
        });

 it("should be able to answer a question with yes", function() {
                var button = $("#btnYes", view.render().el);
                expect(button.length).toBe(1);

                spyOn(view, 'answerYes');

                button.click();
                expect(view.answerYes).toHaveBeenCalled();

            });
  

Однако приведенное выше определение представления создает метод answerYes в прототипе proto , но шпион создает функцию для фактического экземпляра в представлении, поэтому в итоге я получаю view.answerYes(), который является шпионом и view.__proto__.answerYes , который я на самом делехотите шпионить.

Как я могу создать spy, чтобы он переопределял метод answerYes определения представления?

Комментарии:

1. почему вы хотите шпионить за методом прототипа? у вас есть экземпляр представления, и вы тестируете его на этом экземпляре. вы должны следить за экземпляром.

2. Единственная причина, по которой я думал, что это может понадобиться, заключается в том, что тест никогда не проходил, когда я использовал view.answerYes

Ответ №1:

Привет, у меня сегодня была такая же проблема. И я только что нашел решение: после создания метода spyed (answerYes) вам необходимо обновить события представления, чтобы вызвать этот новый метод spyed 😉 :

[...]

 spyOn (просмотр, 'answerYes');
 view.delegateEvents();

 кнопка.нажмите ();
 ожидайте (view.answerYes).toHaveBeenCalled();

[...]

Дополнительная информация о событиях делегирования

Получайте удовольствие!

Комментарии:

1. это помогло! но есть ли лучший способ сделать это, или мне всегда нужно вызывать delegateEvents(), когда я слежу за методами представления? (стандартная практика?)

2. Большое вам спасибо! После нескольких часов проб и ошибок я нашел этот пост. И FWIW это можно использовать с Sinon.stub()

Ответ №2:

Обычно мне нравится предполагать, что код фреймворка уже выполняет то, что должен, и только проверяет мое его использование, поэтому я считаю приемлемым иметь тест, проверяющий хэш событий. Если я обнаружу, что дублирую функциональность магистрали, чтобы протестировать свою вещь (например, делегирование событий), то, возможно, я на шаг ближе к интеграционным тестам, чем мне действительно нужно. Я также активно использую прототип, чтобы быть супер-изолированной леди в своих модульных тестах. Конечно, по-прежнему важно иметь интеграционный уровень, который выполняет все эти упражнения, но я нахожу цикл обратной связи слишком длинным для фазы тестового вождения.

Ответ №3:

Это создает шпионский answerYes метод для PromptView :

 spyOn(PromtView.prototype, 'answerYes');
  

Ответ №4:

TL; DR: шпионить за методом экземпляра, а не за прототипом.

Я думаю, вам нужно настроить свой тест по-другому. В блоке слишком много проблем и слишком много ожиданий it , и вы также загрязняете глобальное пространство имен, что может вызвать проблемы с тестами.

 beforeEach(function() {
  loadFixtures('promptPage.tmpl');

  var model = new PromptModel();
  this.view = new PromptView({model:model});
  this.view.render();

  this.button = this.view.$("#btnYes");
});

it("should render the button", function(){
  expect(this.button.length).toBe(1);
});

it("should be able to answer a question with yes", function() {
  spyOn(this.view, 'answerYes');

  this.button.click();
  expect(this.view.answerYes).toHaveBeenCalled();
});
  

Вам не нужно строго ожидать длину кнопки. Если кнопка не имеет длины (не найдена), вы получите другие сбои. Но вы можете захотеть, чтобы он был там, чтобы было легче понять, что представление отображалось неправильно.

Вы также должны следить за view экземпляром, как вы это делали. Определение PromptView добавляет answerYes метод к прототипу, да, но тот, за которым вы хотите следить, — это экземпляр представления, а не прототип.

Если вы следите за методом прототипа, то каждый раз, когда вы пытаетесь использовать это представление в своих тестах, answerYes метод будет шпионским, а не фактическим методом. Это может показаться приятным, но это вызовет проблемы, поскольку у вас не будет доступа к действительным шпионским данным при многократном вызове этого метода. Он просто будет накапливать все вызовы для этого одного шпиона. Если вы попытаетесь шпионить за методом прототипа дважды, вы можете получить шпиона шпиона, что было бы странно и могло вызвать проблемы.

Комментарии:

1. Дерик подсказка об изменении структуры — приятный штрих, но в тесте нет существенных изменений, кроме объявления представления об этом . Однако, когда я запускаю этот тест, я все равно получаю предупреждение, вместо того, чтобы запускать spy execute. Ожидание также терпит неудачу, и я не понимаю, почему.

2. Возможно ли, что привязка магистрали каким-то образом влияет на результат?

Ответ №5:

Если у вас возникли проблемы с использованием spyOn, вы можете подумать о создании шпиона. Итак, что-то вроде:

 var eventSpy;
eventSpy = jasmine.createSpy('eventSpy');
view.$el.on('myCustom:event', eventSpy);