Extensions to Jasmine unit testing to transparently support Promises in async testing scenarios
So you really like Jasmine. But you also really like promises. And you'd like to see support in Jasmine for the promise-returning test style; similar to the great work by Domenic Denicola for the Mocha as Promised and others.
This library provides an extension of the Jasmine
Spec::runs() to support Promises
and will auto-magically wait for the promise to resolve or reject before continuing with subsequent unit tests.
Best of all... it supports AngularJS and will signficantly reduce the size of your test code (see bottom).
Until now you've been making do with manual solutions that explicity use
to force the test runner to pause the tests while waiting for the async response.
Here is a sample of code constructed in the tradition, old way. For purposes of code samples, let's consider API tests where the
is asynchronous and returns a promise instance:
it "should respond successfully for valid authors"var ready = falseresult;runsauthorsvalidate"Domenic Denicola"thenresult = data;ready = true; // continue test runnerready = true; // continue test runner;;// Pause test runner until timeout or yourAsyncCall() respondswaitsForreturn result;;// Run the code that checks the expectations…runsexpect resultvalid toBeEqual 1 ;expect resultlevel toBeEqual "awesome" ;;;
Developers will immediately note that this traditional approach is verbose and error-prone when developers are need to create many tests for their async APIs.
With Jasmine-As-Promised and APIs that return promises, consider the code tersity and simplicity that can be realized when your unit tests return Promises:
it "should be respond for valid authors"runsreturn authenticatorvalidate"Domenic Denicola"thenexpect resultvalid toBeEqual 1 ;expect resultlevel toBeEqual "awesome" ;;;;
You could even separate your
expect() calls if wanted. Instead of nesting your expectations inside
the promise handler, consider another supported approach:
it "should respond successfully for valid authors"runsreturn authenticatorvalidate"Domenic Denicola" ;expect resultvalid toBeEqual 1 ;expect resultlevel toBeEqual "awesome" ;;;
With this new approach developers no longer need to worry about
waitsFor(), latch methods, etc. With Jasmine as Promised, you have a much two (2) much nicer, easily applied options available!
Once you install and set up Jasmine-as-Promised, you now have a second way of creating asynchronous tests, besides Jasmine's
runs(); waitsFor(); runs(); style. Just return a promise. When the promise is resolved the test expectations are checked and if it is rejected the test
fails, with the rejection reason as the error. Nice, huh?
Jasmine as Promised works with all Jasmine interfaces: BDD, TDD, QUnit, whatever. It hooks in at such a low level, the interfaces don't even get involved.
Jasmine-Node is a project that integrates the Jasmine Spec framework with NodeJS.
jasmine-node includes an alternate syntax for writing asynchronous tests. Accepting a done callback in the specification will trigger jasmine-node to run the test asynchronously waiting until the done() callback is called.
var request = require'request';it"should respond with hello world"request""expectbodytoEqual"hello world";done;;;
Notice that this
Jasmine-as-Promised library does not use a done callback function argument in the
it( ) call. But developers can still use Jasmine-As-Promised with Jasmine-Node; see the usage notes below for use with Node.
npm install jasmine-as-promised --save-dev to get up and running. Then:
You can of course put this code in a common test fixture file; for an example, see the Jasmine as Promised tests.
Jasmine-as-Promised supports being used as an AMD module, registering itself anonymously. So, assuming you have
configured your loader to map the Jasmine and Jasmine as Promised files to the respective module IDs
"jasmine-as-promised", you can use them as follows:
definevar jasmine = require"jasmine";var jasmineAsPromised = require"jasmine-as-promised";jasmineAsPromisedjasmine;;
If you include Jasmine-as-Promised directly with a
<script> tag, after the one for Jasmine itself, then it will
automatically plug in to Jasmine and be ready for use:
require("jasmine-as-promised")() above tries to detect which instance of Jasmine is being used automatically. This
way, Jasmine-as-Promised can plug into either the local Jasmine instance installed into your project, or into the global
Jasmine instance if you're running your tests using the globally-installed command-line runner.
In some cases, if you're doing something weird, this can fall down. In these cases, you can pass the Jasmine instance into
into the Jasmine-as-Promised function. For example, if you somehow had your Jasmine module as a property of the
instead of it being found in the usual npm directory structures, you would do
Now you can use
Bower (the package manager for the web) to get the most recent released version of the library installed in your project-relative
bower install jasmine-as-promised
And Bower will also auto-install the
Jasmine library for you; as part of this library install.
While this approach using the interceptor or head hook approach, it should be note that this is hack... albeit a reasonable one.
Note that Jasmine-as-Promised just overrides the
window.runs method (published as part of Jasmine core); check the source for more details.
When using AngularJS with Jasmine Specs (and Jasmine-as-Promised ), developers have two (2) types of test scenarios:
- Testing with angular-mocks to use mock $http and mock $timeout(s).
- Testing with angular-spec to use Jasmine tests with LIVE $http (real XHR) calls.
Developers should study the test Specs prepared within test_withAngular.html. That file shows you how:
- script libraries should be loaded (for tests)
- Jasmine should be started
- angular services can be constructed with mock APIs
- Spec(s) can be implemented using
- Angular Promise-based services can be easily tested.
Developers should note that these do NOT show how RequireJS can also be used… that is out-of-scope for this project. If you really need to know how to use RequireJS with your tests and Karma, contact me or come and see me at the 2014 NG-Conference