In a Jasmine test for an Angular app, I stubbed a method that returns a $q promise. The test kept timing out even though I didn't forget about done().

Versions used below

"angular": "1.3.8",
"jasmine-core": "2.1.3"

Digest cycle needed

Just like how a digest cycle is needed for Angular to notice changes to scope properties, a digest is needed for $q promise chain propagation.

When necessary, you can trigger a digest cycle by calling $rootScope.$apply().

Test with timeout fix

describe('beginGame()', function() {
var gameService, $rootScope;

beforeEach(inject(function(_gameService_, _$rootScope_, $q) {
gameService = _gameService_;
$rootScope = _$rootScope_;

// Stub method so it just returns a resolved promise.
// The controller being tested uses gameService.
spyOn(gameService, 'start').and.callFake(function(game) {
return $q(function(resolve, reject) {

it('starts a game with given players', function(done) {
controller.addPlayers(['jill', 'joe', 'jen']);

// here's the $q promise chain

function assertGameStarted() {
playerNames: ['jill', 'joe', 'jen']

// manually trigger digest cycle

Without line 32 the promise chain doesn't propagate, done() is never called and the test fails due to timeout.

Why $q works fine in app code

Digest cycles are triggered for you behind the scenes. Angular built-ins (ngClick, $timeout, etc) call $rootScope.$apply() which triggers a digest cycle.