Rails + Jasmine-Ajax: какой правильный способ проверить код, вызванный `ajax: success` (jquery-ujs)
Я пытаюсь протестировать определенную внутреннюю библиотеку, которая имеет некоторый JS-режим, срабатывающий в событии ajax:success
.
Библиотека создает ссылку, которая выглядит так:
<%= link_to 'click here', '/some_path', class: 'special-link', remote: true %>
И в JS-части библиотеки есть код привязки событий, , который является частью, которую я хочу проверить с помощью черного ящика через ее эффект на DOM:
$(document).on 'ajax:success', '.special-link', (e, data, status, xhr) ->
# Code that has some effect on the DOM as a function of the server response
Библиотека работает как ожидается в браузере. Однако, когда я пытаюсь протестировать библиотеку в Жасмине, вызывая $('.special-link').click()
, желаемого эффекта на DOM не наблюдается.
Проблема, похоже, в том, что событие ajax:success
не запускается:
describe 'my library', ->
beforeEach ->
MagicLamp.load('fixture') # Fixture library that injects the link above to the DOM
jasmine.Ajax.install()
jasmine.Ajax.stubRequest('/some_path').andReturn({
responseText: 'response that is supposed to trigger some effect on the DOM'})
afterEach ->
jasmine.Ajax.uninstall()
# Works. The fixtures are loading properly
it '[sanity] loads fixtures correctly', ->
expect($('.special-link').length).toEqual(1)
# Works. The jquery-ujs correctly triggers an ajax request on click
it '[sanity] triggers the ajax call', ->
$('.special-link').click()
expect(jasmine.Ajax.requests.mostRecent().url).toContain('/some_path')
# Works. Code that tests a click event-triggering seems to be supported by Jasmine
it '[sanity] knows how to handle click events', ->
spy = jasmine.createSpy('my spy')
$('.special-link').on 'click', spy
$('.special-link').click()
expect(spy).toHaveBeenCalled()
# Does not work. Same code from above on the desired `ajax:success` event does not work
it 'knows how to handle ajax:success events', ->
spy = jasmine.createSpy('my spy')
$('.special-link').on 'ajax:success', spy
$('.special-link').click()
expect(spy).toHaveBeenCalled()
Каков правильный способ проверить влияние на DOM кода, который выполняется в событиях ajax:success
?
Ответы
Ответ 1
Вы пробовали просто следить за функцией ajax
? Для этого вам нужно использовать spyOn
и заставить его вызвать обработчик события success
. Это позволит вам проверить, что вы ожидаете, когда оно будет вызвано.
it 'knows how to handle ajax:success events', ->
spyOn($, "ajax").and.callFake( (e) ->
e.success({});
)
$('.special-link').click()
# expect some method to be called or something to be changed in the DOM
Ответ 2
Вот как мы будем обращаться с такими вещами в моей команде.
it 'knows how to handle ajax:success events', ->
spyOn($.fn, 'on');
$('.special-link').click()
expect($.fn.on).toHaveBeenCalledWith('ajax:success',
'.special-link'
some_func);
Этот шаблон хорошо подходит для тестирования других событий 'on'. Скажем, у нас есть несколько jQuery:
$document.on('myCustomEvent', '.some_selector', somecode.custom_func);
$document.on('ajax:error', '.some_selector', somecode.failure_func);
Затем мы можем протестировать его с помощью этого шаблона:
beforeEach ->
spyOn($.fn, 'on');
somecode.init();
Тестирование отказа Ajax
it('indicates failure after ajax error', ->
expect($.fn.on).toHaveBeenCalledWith('ajax:error',
'.some_selector',
somecode.failure_func);
Тестирование Ajax вызывается из настраиваемого события
it('indicates ajax call from custom event', ->
expect($.fn.on).toHaveBeenCalledWith('myCustomEvent',
'.some_selector',
somecode.custom_func);
Ответ 3
После много отладки я нашел решение.
Когда я отправил свой вопрос, я сделал 3 критических ошибки.
Ошибка №1: jasmine.Ajax.stubRequest
путь не относительный
Вызов Ajax не был правильно закодирован, поскольку при тестировании в браузере путь должен быть не относительным /some_path
, а абсолютным http://localhost:3000/some_path
.
Другими словами, вместо:
jasmine.Ajax.stubRequest('/some_path')
Я должен был использовать версию regexp:
jasmine.Ajax.stubRequest(/.*\/some_path/)
Ошибка №2: jasmine.Ajax.andReturn
должна включать cotentType
Вместо:
jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({
responseText: 'response that is supposed to trigger some effect on the DOM'})
Я должен был сделать:
jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({
contentType: 'text/html;charset=UTF-8',
responseText: 'response that is supposed to trigger some effect on the DOM'})
Без него ajax:error
запускается, а не ajax:success
, с parseerror
.
Ошибка №3: Обработчик ajax:success
называется async-ly
Эти строки кода:
spy = jasmine.createSpy('my spy')
$('.special-link').on 'ajax:success', spy
$('.special-link').click()
expect(spy).toHaveBeenCalled()
не работают, поскольку обработчик ajax:success
, вызывающий spy()
, вызывается асинхронно после достижения expect(spy).toHaveBeenCalled()
. Подробнее об этом можно узнать в документации Jasmine.
Объединяя все вместе
Это код, который работает, фокусируясь только на последнем it
заявлении, которое было основным намерением оригинального вопроса:
describe 'my library', ->
beforeEach ->
MagicLamp.load('fixture') # Fixture library that injects the link above to the DOM
jasmine.Ajax.install()
jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({
contentType: 'text/html;charset=UTF-8',
responseText: 'response that is supposed to trigger some effect on the DOM'})
afterEach ->
jasmine.Ajax.uninstall()
# Restructuring the original `it` statement to allow async handling
describe 'ajax:success event handling', ->
spy = jasmine.createSpy('spy')
# Ensures no `it` statement runs before `done()` is called
beforeEach (done) ->
$('.special-link').on 'ajax:success', ->
spy()
done()
$('.special-link').click()
it 'knows how to handle ajax:success events', ->
expect(spy).toHaveBeenCalled()
Надеюсь, что это поможет другим.