Jasmine jQuery
We’re doing a lot of jQuery based development, while Jasmine by design tries to stay DOM agnostic. It is not that big problem, as Jasmine is really flexible and open (I’ll show in a moment how to manipulate DOM with jQuery using plain-vanilla Jasmine), but with a couple of tweaks packed into my jasmine-jquery plugin, jQuery users can make their life even easier.
Jasmine-jquery provides two extensions for Jasmine JavaScript Testing Framework:
A set of custom matchers for jQuery framework
An API for handling HTML, CSS, and JSON fixtures in your specs
Installation
Simply download jasmine-jquery.js from here and include it in your Jasmine’s test runner file (or add it to jasmine.yml file if you’re using Ruby with jasmine-gem). Remember to include also jQuery library as jasmine-jquery relies on it.
For Ruby on Rails, use this gem or recommend to comply with the standard RSpec and Jasmine frameworks dir structure and keep your tests in spec/javascripts/ dir. Put jasmine-jquery (and other libraries like jasmine-ajax) into spec/javascripts/helpers dir (so they are automatically loaded) and fixtures into spec/javascripts/fixtures dir.
You now have jasmine and jasmine-jquery downloaded, you will now need to update the SpecRunner.html file to work with jasmine-jquery. It’s not too difficult just a case of adding a couple of script tags
You need to add the jasmine-jquery.js file on the line after the jasmine-html.js file like so:
<script type="text/javascript" src="lib/jasmine-jquery.js"></script>
And as we are testing jQuery you need to add jQuery to you page, which you will be familiar with. You add this on the line below jasmine-jquery.js like so:
<script type="text/javascript" src="lib/jquery.js"></script>
Now we have the testing lib’s all set up we need to add our test specs and jQuery plug-in so we can run test’s against it.
jQuery matchers
Jasmine-jquery provides following custom matchers (in alphabetical order):
toBeChecked()
: only for tags that have checked attribute
expect($(‘<input type=”checkbox” checked=”checked”/>’)).toBeChecked()
toBeDisabled()
:
expect($(‘<input type=”submit” disabled=”disabled”/>’)).toBeDisabled()
toBeEmpty()
: Checks for child DOM elements or text.toBeFocused()
:
expect($(‘<input type=”text” />’).focus()).toBeFocused()
toBeHidden()
:toBeInDOM()
: Checksto see if the matched element is attached to the DOMtoBeMatchedBy(jQuerySelector)
: Check to see if the set of matched elements matches the given selectortoBeSelected()
: only for tags that have selected attributetoBeVisible()
: Elements are considered visible if they consume space in the document. Visible elements have a width or height that is greater than zero.toContainElement(jQuerySelector)
toContainHtml(string)
toContainText(string)
toEqual(jQuerySelector)
toExist()
: true if element exists in or out of the domtoHandle(eventName)
toHandleWith(eventName, eventHandler)
toHaveAttr(attributeName, attributeValue)
: attribute value is optional, if omitted it will check only if attribute existstoHaveBeenTriggeredOn(selector)
: if event has been triggered on selectortoHaveBeenTriggered()
: if event has been triggered on selectortoHaveBeenTriggeredOnAndWith(selector, extraParameters)
: if event has been triggered on selector and with extraParameterstoHaveBeenPreventedOn(selector)
: if event has been prevented on selectortoHaveBeenPrevented()
: if event has been prevented on selectortoHaveClass(className)
toHaveCss(css)
toHaveData(key, value)
toHaveHtml(string)
toHaveId(id)
toHaveLength(value)
toHaveProp(propertyName, propertyValue)
: property value is optional, if omitted it will check only if property existstoHaveText(string)
: accepts a String or regular expressiontoHaveValue(value)
: only for elements on which val can be called (input, textarea, etc)
HTML Fixtures
The Fixture module of jasmine-jquery allows you to load HTML content to be used by your tests. The overall workflow is like follows:
In myfixture.html file:
<div id="my-fixture">some complex content here</div>
By default, fixtures are loaded from spec/javascripts/fixtures. You can configure this path: jasmine.getFixtures().fixturesPath = ‘my/new/path’;.
To keep your tests clean, you may want to reuse fixtures not only between tests in one file, but also between different test files. The answer is loading fixtures from files
describe('test with fixture files', function () { beforeEach(function () { $('#fixture').remove(); $.ajax({ async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded dataType: 'html', url: 'my_fixture.html', success: function(data) { $('body').append($(data)); } }); }); it('should use DOM fixture', function () { $('#fixture').myTestedJqueryPlugin(); expect($('#fixture')).toSomething(); }); it('should use DOM fixture again', function () { $('#fixture').myTestedJqueryPlugin(); expect($('#fixture')).toSomethingElse(); }); });
The fixture file is reloaded via AJAX before each of your tests, which results in a major penalty to your test suite’s speed – some kind of fixture caching would be welcome. Also, what if you want to load more than one fixture? You’ll then have to duplicate fixture loading and DOM clean-up code. And what if for some reason you want to load fixture from external file, but don’t want to append it automatically to DOM, but rather assign to a variable inside your test? Again, this can be easily done, but it will add another variation to your beforeEach code. Even worse, you’ll probably use fixtures in more than one test file, so your fixture loading code will end-up copy pasted all over the place. As you can see this can quickly go out of control for any non trivial project.
Fixtures are automatically appended to DOM by loadFixtures method and automatically cleaned-up between tests.
Jasmine-jQuery to the reuse
It extends Jasmine framework with a robust set of methods to handle HTML fixtures. It supports also fixture caching to speed-up your test suite and auto clean-up of DOM between tests. With jasmine-jquery you can write your tests like this:
describe('test with jasmine-jquery', function () { it('should load many fixtures into DOM', function () { loadFixtures('my_fixture_1.html', 'my_fixture_2.html'); expect($('#jasmine-fixtures')).toSomething(); }); it('should only return fixture', function () { var fixture = readFixtures('my_fixture_3.html'); expect(fixture).toSomethingElse(); }); });
Apart from loadFixtures and readFixtures jasmine-jquery provides other useful methods, so don’t forget to check jasmine-jquery GitHub page for the full documentation. You can try Jasmine Online here.
Reference
[1] – Jasmine
[2] – Jasmine-jQuery
[3] – http://testdrivenwebsites.com/2010/07/29/html-fixtures-in-jasmine-using-jasmine-jquery/