[英]Rails + Jasmine-Ajax: what is the correct way to test code triggered by `ajax:success` (jquery-ujs)
I am trying to test a certain internal library that has some JS behavior triggered on the ajax:success
event. 我正在尝试测试某个内部库,该库在
ajax:success
事件上触发了一些JS行为。
The library creates a link that looks like this: 该库创建一个如下所示的链接:
<%= link_to 'click here', '/some_path', class: 'special-link', remote: true %>
And in the JS part of the library there is event binding code, which is the part I want to black-box test through its effect on the DOM : 在库的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
The library works as expected in the browser. 该库在浏览器中按预期工作。 However, when I try to test the library in Jasmine by calling
$('.special-link').click()
, the desirable effect on the DOM cannot be observed. 但是,当我尝试通过调用
$('.special-link').click()
来测试Jasmine中的库时$('.special-link').click()
,无法观察到对DOM的理想效果。
The issue, it seems, is that the ajax:success
event does not get triggered: 看来,问题是
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()
What is the right way to test the effect on the DOM of code that runs in ajax:success
events? 测试在
ajax:success
中运行的代码的DOM效果的正确方法是什么ajax:success
事件?
Have you tried simply spying on the ajax
function? 你试过简单地监视
ajax
功能吗? For that, you need to use spyOn
and force it to call the success
event handler. 为此,您需要使用
spyOn
并强制它调用success
事件处理程序。 That will let you test what you expect to happen when it's called. 这将让你测试你期望在它被调用时发生的事情。
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
Here's how we would handle this sort of thing on my team. 以下是我们在团队中处理此类事情的方法。
it 'knows how to handle ajax:success events', ->
spyOn($.fn, 'on');
$('.special-link').click()
expect($.fn.on).toHaveBeenCalledWith('ajax:success',
'.special-link'
some_func);
This pattern extends well to testing other 'on' events, too. 这种模式也很适合测试其他“开启”事件。 Say we have some jQuery like this:
假设我们有一些像这样的jQuery:
$document.on('myCustomEvent', '.some_selector', somecode.custom_func);
$document.on('ajax:error', '.some_selector', somecode.failure_func);
Then we can test it using this pattern: 然后我们可以使用这种模式测试它:
beforeEach ->
spyOn($.fn, 'on');
somecode.init();
Testing an Ajax failure 测试Ajax失败
it('indicates failure after ajax error', ->
expect($.fn.on).toHaveBeenCalledWith('ajax:error',
'.some_selector',
somecode.failure_func);
Testing Ajax was called from custom event 测试Ajax是从自定义事件调用的
it('indicates ajax call from custom event', ->
expect($.fn.on).toHaveBeenCalledWith('myCustomEvent',
'.some_selector',
somecode.custom_func);
After a lot of debugging, I found a solution. 经过大量的调试,我找到了解决方案。
When I posted my question, I made 3 critical mistakes. 当我发布我的问题时,我犯了3个重大错误。
jasmine.Ajax.stubRequest
path is not relative jasmine.Ajax.stubRequest
路径不是相对的 The Ajax call was not stubbed correctly, since the path should not be the relative /some_path
but rather the absolute http://localhost:3000/some_path
when testing from the browser. Ajax调用没有正确存根,因为路径不应该是相对
/some_path
,而是从浏览器进行测试时的绝对http://localhost:3000/some_path
。
In other words, instead of: 换句话说,而不是:
jasmine.Ajax.stubRequest('/some_path')
I should have used the regexp version: 我应该使用正则表达式版本:
jasmine.Ajax.stubRequest(/.*\/some_path/)
jasmine.Ajax.andReturn
must include cotentType jasmine.Ajax.andReturn
必须包含cotentType Instead of: 代替:
jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({
responseText: 'response that is supposed to trigger some effect on the DOM'})
I should have done: 我应该做的:
jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({
contentType: 'text/html;charset=UTF-8',
responseText: 'response that is supposed to trigger some effect on the DOM'})
Without it, the ajax:error
is triggered rather than ajax:success
, with a parseerror
. 没有它,
ajax:error
被触发而不是ajax:success
,带有parseerror
。
ajax:success
handler is called async-ly ajax:success
处理程序被称为async-ly These lines of code: 这些代码行:
spy = jasmine.createSpy('my spy')
$('.special-link').on 'ajax:success', spy
$('.special-link').click()
expect(spy).toHaveBeenCalled()
don't work, since the ajax:success
handler that calls spy()
is called asynchronously after expect(spy).toHaveBeenCalled()
is reached. 不起作用,因为调用
spy()
的ajax:success
处理程序在到达expect(spy).toHaveBeenCalled()
后异步调用。 You can read more about this in Jasmine documentation . 您可以在Jasmine文档中阅读更多相关信息。
This is the code that works, focusing only on the last it
statement which was the main intent behind the original question: 这是一个工作,只注重最后的代码
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()
Hope this helps others. 希望这有助于其他人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.