jasmine_vertical

Jasmine is a framework to create tests for JavaScript code in the behavior-driven development style. In this article, you will learn how to integrate Jasmine into a Ruby on Rails application.

Jasmine has a clean syntax with the aim to provide descriptive behavior about the code being tested. People that use RSpec or Minitest::Spec will be familiar with Jasmine. Even if you haven’t used either of those frameworkes, you’ll have no difficulties in creating tests for Jasmine.

After reading this article, you’ll be able to integrate Jasmine into your Rails application, create tests for your JavaScript files, test your Rails UJS functionality inside your app, and be able to run Jasmine as a part of your existing (right??) Continuous Integration existing build system.

Installation

To have a working Jasmine installation in a Rails application, you need to put the jasmine-gem into the Gemfile. Make sure it’s inside development and test group as follows:

group :development, :test do
  gem "jasmine"
end

Then run the following step to install the gem:

bundle install

Once the gem installation finishes, run the Jasmine generator as follows:

rails generate jasmine:install

The generator will output something like this on the terminal:

create  spec
create  spec/javascripts/helpers/.gitkeep
create  spec/javascripts/support/jasmine.yml
create  spec/javascripts/support/jasmine_helper.rb

These are the necessary files for jasmine to run. Detailed information about configuring your jasmine installation will be explained in the configuration section.

Usage

Once the installation steps are finished, you should be able to run Jasmine. Jasmine can be run in several different ways: you can run Jasmine in your browser or in continuous integration (CI) mode. The benefit of using the CI mode is that you can integrate it to your build system.

Run in Browser

Before you can run Jasmine in your browser, you need to start the Jasmine server:

rake jasmine

Then point your browser to the following URL:

http://localhost:8888/

To have a look at how the test results look, the gem provides another generator to create test examples. From another terminal session, run the following command:

rails generate jasmine:examples

Below is a screenshot of the generated example test results:

jasmine-tests-result

You can see that there is a button on the top right corner of the page. It has several options that change how Jasmine runs the tests. Let’s get into the available options explained in detail.

Raise Exceptions

By clicking this option, it will redirect to the following URL:

http://localhost:8888/?catch=false

This option disables error catching in the JavaScript source code and in the Jasmine test file. Without this option, all errors in the test and source files are wrapped in a catch block.

The benefit of enabling this option is to allow Jasmine test results to be broken intentionally if there is an error. Therefore, you know the failure is due to an error, not a failing expectation.

Stop Spec on Expectation Failure

By clicking this option, it will redirect to the following URL:

http://localhost:8888/?throwFailures=true

This option stops failing specs immediately. Let’s take a look at the following code example:

it("should not be possible to play", function() {
  player.stop();
  expect(player.isPlaying).toBeFalsy();
  expect(player.currentlyPlayingSong).not.toEqual(song);
});

Without this option, failing spec outputs all expectation failure messages for the failing spec:

..F..
Failures:
          Player when song has been paused should be possible to resume

          Expected true to be falsy.
          Error: Expected true to be falsy.

          Expected Song({  }) not to equal Song({  }).
          Error: Expected Song({  }) not to equal Song({  }).

With this option, failing spec only outputs the first expectation failure message and then stop the execution of the failing spec.

..F..
Failures:
          Player when song has been paused should be possible to resume

          Expected true to be falsy.
          Error: Expected true to be falsy.

Run Tests in Random Order

By clicking this option, it will redirect to the following URL:

http://localhost:8888/?random=true

This option enables the test to be run in a random sequence every time the test runs. Without this option, all tests will be run in the same sequence every time the test is running.

The benefit of enabling this option is to reveal dependencies between tests, therefore, you can reduce test dependencies and each test will have good isolation.

Run as Continuous Integration

You can integrate Jasmine into your Continuous Integration workflow by using a headless browser. Fortunately, the gem already supports integration with a headless browser.

To run Jasmine in CI mode, run it as follows:

rake jasmine:ci

The headless browser integration uses PhantomJS. If you don’t have PhantomJS installed in your system, it will try to download it before you can run it in CI mode.

The above command outputs something like this:

Waiting for jasmine server on 49948...
jasmine server started
.....
5 specs, 0 failures

By default jasmine will attempt to find a random open port in CI mode. To set the port, you can configure the port in jasmine_helper.rb:

Jasmine.configure do |config|
   config.ci_port = 1234
end

Configuration

The default configuration should serve you just fine. But, if you need to have a different configuration, there are two files that can be used to accomplish this: jasmine.yml and jasmine_helper.rb.

Before we move any further, you need to know that some configuration options don’t apply if run from the browser. In that case, use the “Options” menu when running from the browser:

Jasmine load configurations with the following sequence:

  1. Jasmine reads jasmine.yml configuration file first.
  2. Jasmine reads jasmine_helper.rb and overrides configuration defined by
    jasmine.yml.

jasmine.yml

Here is an example of the available configuration in jasmine.yml with default values provided:

# spec/javascripts/support/jasmine.yml
random: false
show_console_log: false
stop_spec_on_expectation_failure: false

jasmine_helper.rb

Here is an example of the available configuration in jasmine_helper.rb with default value provided:

# spec/javascripts/support/jasmine_helper.rb
Jasmine.configure do |config|
  config.random = false
  config.show_console_log = false
  config.stop_spec_on_expectation_failure: false

  config.show_full_stack_trace = false
  config.prevent_phantom_js_auto_install = false
  config.server_port = 8888

  # ci port is random by default
  # config.ci_port = 1234
end

Testing

Testing JavaScript files using Jasmine in a Rails application should be fairly simple. It uses the same standards as Jasmine in general, but there are things that needs to be considered specific to a Jasmine installation in Rails.

Testing JavaScript

Test files for JavaScript in a rails application reside in the spec/javascripts folder. For each javascript file, you need to put the test file in the same path as the file. For example, if you have the following javascript file in your app:

app/assets/javascripts/jasmine_examples/Player.js

You place the spec file in the following path:

spec/javascripts/jasmine_examples/PlayerSpec.js

Jasmine will include the test on the next test run. There is no configuration to have your test run. To test your JavaScript file, you need to refer to Jasmine Documentation.

Rails UJS

Rails UJS can be tricky to test, because the response can be in any format, such as JavaScript, JSON, or HTML. Regardless of the response format, you can still test Rails UJS using Jasmine.

There are 3 parts you need to identify:
– The UJS link.
– The response.
– The side effect.

Once you identify the above requirements you can create dummy response, trigger the UJS, and assert the side effect. As an example, the following code is testing a UJS link that removes a DOM element from the page:

it("removes the associated element", function(){
  var response = { status: 200 };
  $('a.remove#123').trigger('ajax:success', response, 'xhr');
  expect($('li#123')).toEqual([]);
});

From the above example, triggering a UJS link can be done using ajax:success. Lists of complete events that can be triggered can be seen in the Rails UJS Documentation.

Plugins

Sometimes you need tools that will help increase your productivity. This section serves as a brief introduction for some of the plugins that have been created by the community. For a more detailed information of each plugin, visit the official plugin repository.

Before installing any plugins, you need to properly setup the configuration so plugins can be properly loaded before the tests run. Inside the application root path, create a folder for plugins:

mkdir spec/javascripts/plugins/

And inside jasmine.yml, add the above folder to the helpers section:

helpers:
  - 'plugins/**/*.js'

Jasmine-Jquery

jasmine-jquery provides jQuery related matchers in Jasmine. This matcher provides a lot of handy jQuery related matchers, so you can have better testing productivity. Download the latest release of the plugin
here.

Take a look at the following example:

expect($('<div>some text</div>')).toHaveText(/some/)

As with standard Jasmine matchers, you can also invert it using the .not prefix:

expect($('<div>some text</div>')).not.toHaveText(/other/)

Jasmine-Matchers

Jasmine-Matchers is a tool to provide additional matchers in Jasmine. Besides that, it also provides a more friendly expectation failure message compared to the default. Download the latest release of the plugin here.

The following test uses the Jasmine default matchers:

it('should distribute evenly', function() {
  expect(basket.items % 2 === 0).toEqual(true);
});

And it prints the following failure message:

Expected false to equal true

Using jasmine-matchers, the test looks like:

it('should distribute evenly', function() {
  expect(basket).toHaveEvenNumber('items');
});

With a more friendly failure message:

Expected member "items" of { items : 25 } to be even number.

Jasmine-Fixture

Jasmine-Fixture is a plugin that provides DOM creation using CSS selectors, therefore you can interact with the DOM much easier. It also helps you avoid test pollution by removing DOM created by the plugin after each test run. Download the latest release of the plugin here.

If you are not using jasmine-fixture, you might do this to interact with the DOM:

beforeEach(function(){
  $('<div id="toddler"><div class="hidden toy"><input name="toyName" value="cuddle bunny"></div></div>').appendTo('body');
});

afterEach(function(){
  $('#toddler').remove()
});

Using jasmine-fixture, you just need do this:

beforeEach(function(){
  affix('#toddler .hidden.toy input[name="toyName"][value="cuddle bunny"]')
});

Conclusion

Integrating Jasmine into a Rails application is enabled by the jasmine-gem. The features provided by the gem give flexibility on how to run the tests, either from a browser or as continuous integration optoin. Jasmine can also be configured to your needs, such as running test behavior, and configuring plugins. The use of plugins such as jasmine-jquery, jasmine-matchers, and jasmine-fixtures can help your JavaScript testing to be more productive.

Tags: jasmine, Testing
Hendra is the software engineer of vidio.com, the largest video sharing website in Indonesia. He also contributes to the open source community on rspec-authorization and minitest-around.

No Reader comments

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!