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:
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:
- Jasmine reads jasmine.yml configuration file first.
- 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.
Frequently Asked Questions (FAQs) about Integrating Jasmine into Rails for JavaScript Testing
What is Jasmine and why is it important for Rails testing?
Jasmine is a behavior-driven development (BDD) framework for testing JavaScript code. It does not rely on browsers, DOM, or any JavaScript framework, making it well-suited for testing simple JavaScript code or complex Rails applications. Jasmine provides an easy-to-read syntax and a comprehensive suite of assertions to ensure your code behaves as expected. It’s important for Rails testing because it allows you to write tests in a language that’s familiar to your application code, ensuring seamless integration and reliable results.
How do I install Jasmine into my Rails application?
To install Jasmine into your Rails application, you need to add the jasmine-rails gem to your Gemfile. After adding the gem, run the bundle install command to install it. Once installed, you can generate the necessary Jasmine configuration files by running the rails generate jasmine:install command. This will create a spec/javascripts directory where you can write your Jasmine tests.
How do I write a basic Jasmine test for my Rails application?
Writing a Jasmine test involves creating a spec file in the spec/javascripts directory. In this file, you define a suite using the describe function, which takes two arguments: a string and a function. The string is a name or title for your spec suite, and the function contains the actual test, or spec. Each spec is defined using the it function, which also takes a string and a function. The string is the title of the spec, and the function is the test itself. Inside the test function, you use expect and matcher functions to define what the expected outcome of the test is.
How do I run Jasmine tests in my Rails application?
To run Jasmine tests in your Rails application, you can use the rake jasmine command. This will start a server and provide you with a URL where you can see the results of your tests. Alternatively, you can run the tests in your browser by visiting /jasmine in your application.
What are some common matchers used in Jasmine tests?
Jasmine provides a variety of matchers that you can use to test your code. Some common ones include toBe (checks for exact equality), toEqual (checks for loose equality), toBeTruthy (checks if a value is truthy), toBeFalsy (checks if a value is falsy), toContain (checks if an array or string contains a specific item or substring), and toHaveBeenCalled (checks if a spy was called).
How can I test asynchronous code with Jasmine?
Jasmine provides the done function for testing asynchronous code. You can pass done as an argument to your test function, and then call done() within your asynchronous function when the async operation is complete. Jasmine will wait until the done function is called before moving on to the next test.
How can I use spies in Jasmine to test my Rails application?
Spies in Jasmine are functions that replace the function they are spying on. They can record how many times they were called, what arguments they were called with, and what they returned. To create a spy, you can use the spyOn function. Once a spy is created, you can use various matchers to test its behavior, such as toHaveBeenCalled and toHaveBeenCalledWith.
How can I integrate Jasmine with other testing tools?
Jasmine can be integrated with a variety of other testing tools to enhance your testing capabilities. For example, you can use Jasmine with Capybara to write integration tests that simulate user interactions with your application. You can also use Jasmine with tools like Karma or Protractor for running tests in real browsers.
How can I debug Jasmine tests in my Rails application?
Debugging Jasmine tests can be done in a similar way to debugging regular JavaScript code. You can use the debugger keyword to set a breakpoint in your test, and then use your browser’s developer tools to step through the code and inspect variables. You can also use console.log statements to print out values for inspection.
How can I ensure my Jasmine tests are effective and reliable?
To ensure your Jasmine tests are effective and reliable, it’s important to write comprehensive tests that cover all possible scenarios and edge cases. You should also aim to write clear, readable tests that accurately describe the behavior they are testing. Regularly running your tests and reviewing their output can help you catch and fix issues early. Additionally, using good testing practices like test-driven development (TDD) and behavior-driven development (BDD) can help improve the quality of your tests.
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.