Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

332
Vistas
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.

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:

$(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.

The issue, it seems, is that the ajax:success event does not get triggered:

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?

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Have you tried simply spying on the ajax function? For that, you need to use spyOn and force it to call the success event handler. 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
over 4 years ago · Santiago Trujillo Denunciar

0

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:

$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

it('indicates failure after ajax error', ->
  expect($.fn.on).toHaveBeenCalledWith('ajax:error',
                                       '.some_selector',
                                       somecode.failure_func);

Testing Ajax was called from custom event

it('indicates ajax call from custom event', ->
  expect($.fn.on).toHaveBeenCalledWith('myCustomEvent',
                                       '.some_selector',
                                       somecode.custom_func);
over 4 years ago · Santiago Trujillo Denunciar

0

After a lot of debugging, I found a solution.

When I posted my question, I made 3 critical mistakes.

Mistake #1: jasmine.Ajax.stubRequest path is not relative

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.

In other words, instead of:

jasmine.Ajax.stubRequest('/some_path')

I should have used the regexp version:

jasmine.Ajax.stubRequest(/.*\/some_path/)

Mistake #2: jasmine.Ajax.andReturn must include 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.

Mistake #3: The ajax:success handler is called 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. You can read more about this in Jasmine documentation.

Putting it all together

This is the code that works, focusing only on the last it statement which was the main intent behind the original question:

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.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda