Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a unit test with Jest for code with Promise

I am trying to write a unit test with Jest and Jasmine-pit for the below code and am totally stumped with it. The code is an ajax call which retrieves some data from resource and saves it in the variable.

init = function() {
    var deferred = Q.defer();
    $.ajax({
        type: 'GET',
        datatype: 'json',
        url: window.location.origin + name,
        success: function (data) {
            userId = data.userId;
            apiKey = data.apiKey;
            deferred.resolve();
        }
    });
    return deferred.promise;
},
like image 564
mu_sa Avatar asked Jan 30 '15 15:01

mu_sa


People also ask

How do you test a Promise in Jest?

You can also use the . resolves matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. return expect(fetchData()).

How do you test Promise reject Jest?

It looks like using try-catch with async/await is the easiest way to achieve this as the rejected value is thrown: it("rejects (bad)", async () => { try { await promiseMe("Error"); } catch (e) { expect(e). toEqual("Error"); } }); But wait.

Can Jest be used for unit testing?

Jest JavaScript resting framework with a focus on simplicity. Jest was created by Facebook engineers for its React project. Unit testing is a software testing where individual units (components) of a software are tested. The purpose of unit testing is to validate that each unit of the software performs as designed.


2 Answers

This tutorial on the website of Jest doesn't answer the question directly, but has the gist of how to unit test promise.

https://facebook.github.io/jest/docs/tutorial-async.html

like image 123
Xuan Avatar answered Sep 17 '22 20:09

Xuan


This frustrated me most of the day today. Here is what I ended up with (testing my ActionCreator (Flux) which uses an API that returns promises and dispatches stuff based on the promise). Basically I mock out the API method that returns the promise and resolve it right away. You'd think that this would be enough to get the .then(...) methods to fire, but the pit code was required to have my ActionCreator actually do work based on the resolved promise.

jest.dontMock('../LoginActionCreators.js');
jest.dontMock('rsvp'); //has to be above the require statement

var RSVP = require('rsvp'); //could be other promise library

describe('LoginActionCreator', function() {
  pit('login: should call the login API', function() {
    var loginActionCreator = require('../LoginActionCreators');
    var Dispatcher = require('../../dispatcher/Dispatcher');
    var userAPI = require('../../api/User');
    var Constants = require('../../constants/Constants');

    //my api method needs to return this
    var successResponse = { body: {"auth_token":"Ve25Mk3JzZwep6AF7EBw=="} };

    //mock out the API method and resolve the promise right away
    var apiMock = jest.genMockFunction().mockImplementation(function() {
      var promise = new RSVP.Promise(function(resolve, reject) {
        resolve(successResponse);
      });

      return promise;
    });
    //my action creator will dispatch stuff based on the promise resolution, so let's mock that out too
    var dispatcherMock = jest.genMockFunction();

    userAPI.login = apiMock;
    Dispatcher.dispatch = dispatcherMock;

    var creds = {
      username: 'username',
      password: 'password'
    };

    //call the ActionCreator
    loginActionCreator.login(creds.username, creds.password);

    //the pit code seems to manage promises at a slightly higher level than I could get to on my 
    // own, the whole pit() and the below return statement seem like they shouldnt be necessary 
    // since the promise is already resolved in the mock when it is returned, but 
    // I could not get this to work without pit.
    return (new RSVP.Promise(function(resolve) { resolve(); })).then(function() {
      expect(apiMock).toBeCalledWith(creds);
      expect(dispatcherMock.mock.calls.length).toBe(2);
      expect(dispatcherMock.mock.calls[0][0]).toEqual({ actionType: Constants.api.user.LOGIN, queryParams: creds, response: Constants.request.PENDING});
      expect(dispatcherMock.mock.calls[1][0]).toEqual({ actionType: Constants.api.user.LOGIN, queryParams: creds, response: successResponse});
    });
  });
});

Here is the ActionCreator which ties the API to the Dispatcher:

'use strict';

var Dispatcher = require('../dispatcher/Dispatcher');
var Constants = require('../constants/Constants');
var UserAPI = require('../api/User');


function dispatch(key, response, params) {
  var payload = {actionType: key, response: response};
  if (params) {
    payload.queryParams = params;
  }
  Dispatcher.dispatch(payload);
}

var LoginActionCreators = {

  login: function(username, password) {
    var params = {
        username: username,
        password: password
    };

    dispatch(Constants.api.user.LOGIN, Constants.request.PENDING, params);

    var promise = UserAPI.login(params);

    promise.then(function(res) {
      dispatch(Constants.api.user.LOGIN, res, params);
    }, function(err) {
      dispatch(Constants.api.user.LOGIN, Constants.request.ERROR, params);
    });
  }
};

module.exports = LoginActionCreators; 
like image 34
pherris Avatar answered Sep 18 '22 20:09

pherris