Read more

Jasmine: Testing AJAX calls that manipulate the DOM

Avatar
Henning Koch
January 17, 2015Software engineer at makandra GmbH

We no longer use jQuery or XMLHttpRequest.

Here is a Javascript function reloadUsers() that fetches a HTML snippet from the server using AJAX and replaces the current .users container in the DOM:

window.reloadUsers = ->
  $.get('/users').then (html) ->
    $('.users').html(html)
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more

Testing this simple function poses a number of challenges:

  • It only works if there is a <div class="users">...</div> container in the current DOM. Obviously the Jasmine spec runner has no such container.
  • The code requests /users and we want to prevent network interaction in our unit test.
  • The AJAX call is asynchronously and we don't have control over when it returns (and hence when it calls the callback function).

Here is a test that does all that:

describe 'reloadUsers', ->

  it 'replaces the user list with a fresh copy from /users', (done) ->

    # Mock network connections
    jasmine.Ajax.install()

    # Create the DOM elements the code works on
    affix('.users').text('old is')

    reloadUsers().then ->
      # Observe the result of the function
      expect('.users').toHaveText('new users')
      # Tell Jasmine that the example is over
      done()

    # Fetch the last AJAX request for inspection
    request = jasmine.Ajax.requests.mostRecent()
    expect(request.url).toMatch(/\/users$/)
    expect(request.method).toBe('GET')
    
    # Fake the response
    request.respondWith
      status: 200
      contentType: 'text/html'
      responseText: 'new users'

Some notes:

  • We need a <div class="users">...</div> containers for the code to work on. We're using jasmine-fixture Show archive.org snapshot to quickly create such a container from a CSS selector by saying affix('.users'). jasmine-fixture will append the container to the document body and clean it up after the test.
  • We need to tell Jasmine to wait until the AJAX response was processed. We do this by accepting an argument done from the it block. We are calling done() once the response has been received and observed by the test.
  • We're using jasmine-ajax Show archive.org snapshot to mock access to the network. jasmine.Ajax.requests.mostRecent() returns the most recent AJAX request, which we can inspect for its parameters and payload. We can also trigger a fake response by calling request.respondWith(...).
  • We're using jasmine-jquery Show archive.org snapshot to get some DOM-related matchers like haveText(...).

See Jasmine: Mocking API requests in an Angular service spec on how to do similar things in an Angular application.

Posted by Henning Koch to makandra dev (2015-01-17 19:42)