id | title | layout | category | permalink | next |
---|---|---|---|---|---|
tutorial |
Tutorial |
docs |
Quick Start |
docs/tutorial.html |
tutorial-jquery |
To begin, let's see how we might test the following function (borrowed from this great article on testing asynchronous functions). It does an Ajax request to get the current user as JSON, transforms this JSON into a new object, and passes it to the callback. Very typical code:
// fetchCurrentUser.js
var $ = require('jquery');
function parseUserJson(userJson) {
return {
loggedIn: true,
fullName: userJson.firstName + ' ' + userJson.lastName
};
}
function fetchCurrentUser(callback) {
return $.ajax({
type: 'GET',
url: 'http://example.com/currentUser',
success: function(userJson) {
callback(parseUserJson(userJson));
}
});
}
module.exports = fetchCurrentUser;
In order to write a test for this module, we need to create a __tests__/
directory where the file fetchCurrentUser.js
is. In this folder, we create a
file called fetchCurrentUser-test.js
and we write our test in it:
// __tests__/fetchCurrentUser-test.js
jest.dontMock('../fetchCurrentUser.js');
describe('fetchCurrentUser', function() {
it('calls into $.ajax with the correct params', function() {
var $ = require('jquery');
var fetchCurrentUser = require('../fetchCurrentUser');
// Call into the function we want to test
function dummyCallback() {}
fetchCurrentUser(dummyCallback);
// Now make sure that $.ajax was properly called during the previous
// 2 lines
expect($.ajax).toBeCalledWith({
type: 'GET',
url: 'http://example.com/currentUser',
success: jasmine.any(Function)
});
});
});
When Jest runs, it runs any tests found in __tests__
directories within the source tree.
The first line is very important: jest.dontMock('../fetchCurrentUser.js');
.
By default, Jest automatically makes all calls to require()
return a mocked
version of the real module – so we need to tell Jest not to mock the file we
want to test or else require('../fetchCurrentUser')
will return a mock.
In our first test, we want to confirm that calling fetchCurrentUser()
properly incurs a call into $.ajax()
with the parameters we expect. To do
this, we just call fetchCurrentUser()
with a dummy callback function, and
then simply inspect the $.ajax
mock to verify that it was called with the
correct parameters.
Woohoo! We've written our first test. But we're not quite done: We would still
like to test that the callback we are passing in is indeed called back when the
$.ajax
request has completed. To test this, we can do the following:
it('calls the callback when $.ajax requests are finished', function() {
var $ = require('jquery');
var fetchCurrentUser = require('../fetchCurrentUser');
// Create a mock function for our callback
var callback = jest.genMockFunction();
fetchCurrentUser(callback);
// Now we emulate the process by which `$.ajax` would execute its own
// callback
$.ajax.mock.calls[0 /*first call*/][0 /*first argument*/].success({
firstName: 'Bobby',
lastName: '");DROP TABLE Users;--'
});
// And finally we assert that this emulated call by `$.ajax` incurred a
// call back into the mock function we provided as a callback
expect(callback.mock.calls[0/*first call*/][0/*first arg*/]).toEqual({
loggedIn: true,
fullName: 'Bobby ");DROP TABLE Users;--'
});
});
In order for fetchCurrentUser
to compute the result to be passed in to the
callback, fetchCurrentUser
will call in to one of it's dependencies: $.ajax
.
Since Jest has mocked this dependency for us, it's easy to inspect all of the
interactions with $.ajax
that occurred during our test.
At this point, you might be wondering how Jest was able to decide what the mock for the jQuery
module should look like. The answer is simple: Jest secretly requires the real module, inspects what it looks like, and then builds a mocked version of what it saw. This is how Jest knew that there should be a $.ajax
property, and that that property should be a mock function.
In Jest, all mock functions have a .mock
property that stores all the
interactions with the function. In the above case, we are reading from
mock.calls
, which is an array that contains information about each time the
function was called, and what arguments each of those calls had.
Now it is time to see if it worked:
> npm test
[PASS] jest/examples/__tests__/fetchCurrentUser-test.js (0.075s)
Woohoo! That's it, we just tested this asynchronous function. One thing to notice is that the code we've written is entirely synchronous. This is one of the strengths of using mock functions in this way: the code you write in tests is always straightfoward and imperative, no matter if the code being tested is synchronous or asynchronous.
The code for this example is available at examples/tutorial/.