Skip to content

Instantly share code, notes, and snippets.

@ThomasBurleson
Last active August 8, 2018 15:24
Show Gist options
  • Save ThomasBurleson/6334259 to your computer and use it in GitHub Desktop.
Save ThomasBurleson/6334259 to your computer and use it in GitHub Desktop.
When using Karma, RequireJS, and AngularJS... demonstrate how Promise(s) callbacks are not auto-triggered when testing with Karma/Jasmine tests. Here we use a Controller and PeopleService with an API that returns Promises. While our mock service resolves the promise, we did that in the context of our test which is `outside` the AngularJS world. …
(function( describe ){
"use strict";
/**
* Demonstration of how to properly use Jasmine testing with AngularJS promise-based services.
*
* NOTE: based on the origina version from Jim Lavin's
* http://codingsmackdown.tv/blog/2012/12/28/mocking-promises-in-unit-tests/
*
* FIXED: Igor Minar fixed this on 8/22/2013. Check with v1.2.x master branch of AngularJS
* https://github.com/IgorMinar/angular.js/commit/8bc169839243728b7dc9279fa31371824efb7afc
*/
define( function()
{
/**
* Typical NG Controller with
*
* @param $scope Injected Presentation Model
* @param delegate Injected UserService w/ async responses
*
* @constructor
*/
var PeopleController = function ($scope, delegate)
{
var onLoadAll = function() {
return delegate
.loadUsers()
.then(function( list )
{
$scope.knownUsers = list;
});
};
// Configure the PM
$scope.knownUsers = [];
$scope.loadPeople = onLoadAll;
};
describe( 'TestSuite for PeopleController', function()
{
var pScope = null,
delegate = null,
/**
* Simulation of button click which triggers PeopleController::loadPeople()
*/
loadPeople = function( )
{
/**
*
* Simulate a button click to trigger loadPeople()
*
* Redirect to the Controller's scope and FLUSH the digest()
* to resolve promises...
*
* While we resolved the promise, we did that in the context of
* our test which is `outside` the AngularJS world. So we have to call digest()
* on the scope’s root to have AngularJS flush the pending deferreds and invoke
* the then() clause in our controller. Once this happens the code in the then()
* clause of our controller will be executed
*
*/
pScope.loadPeople();
pScope.$root.$digest();
};
// ******************************************************
// Setup/TearDown Methods
// ******************************************************
beforeEach( inject( function( $rootScope, $q, $controller )
{
var dfd = $q.defer(),
scope = $rootScope.$new(),
/**
* Mock UserService with internal model and async loadUsers()
* @type {{registry: Array, loadUsers: Function}}
*/
userService = {
registry : [
{
FirstName : "Jim",
LastName : "Lavin",
Email : "jlavin@jimlavin.net",
Bio : "Creator and Host of Coding Smackdown TV"
},
{
FirstName : "Thomas",
LastName : "Burleson",
Email : "ThomasBurleson@gmail.com",
Bio : "Web Solutions Architect and AngularJS Ninja"
}
],
/**
* Mock `loadUsers()` returns `registry`
* @returns {Function|promise}
*/
loadUsers : function()
{
dfd.resolve( userService.registry );
return dfd.promise;
}
};
// Build controller instance and then
// Configure variables for the subsequent tests
$controller(
PeopleController,
{
$scope : scope,
delegate: userService
}
);
delegate = userService;
pScope = scope;
}));
// ******************************************************
// loadPeople() Tests
// ******************************************************
/**
*
*/
it('should call loadUsers() on the UserService when loadPeople() is called', function()
{
spyOn(delegate, 'loadUsers').andCallThrough();
loadPeople();
expect(delegate.loadUsers).toHaveBeenCalled();
});
/**
*
*/
it('should populate the knownUsers when loadPeople() is called', function()
{
loadPeople();
expect( pScope.knownUsers.length ).toBe(2);
});
});
});
})( describe );
@ThomasBurleson
Copy link
Author

The above usage of expect() assertions within a promise resolve handler may NOT work properly from Jasmine testing perspectives. Jasmine may have already assumed the test has completed.

Further research is needed here...

Stay tuned for Jasmine-as-Promised extension that will leverage runs( <promise> ) to block test completion and auto-complete when promise resolves/rejects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment