This answer won't add anything new to those of above, it is only intended to articulate the answer in more detailed way, as it worked for me. When I occurred the issue described in a question above, I spent much time tryng to find a way to make sure all promises had their time to finish and all assertions were asserted.
In my case I had a chain of promises, and after each of them I need to ensure the results do match my expectation. I did not create any promise using deferred
, I rather invoked the existing ones.
So, the thing is that $timeout.flush()
was completely enough for me. My working test looks like this:
describe("Plain command without side-effects", function() {
it("All usecases", inject(function($timeout) {
console.log("All together");
expect(state.number).toEqual(1);
cmdHistory
.execute(increaseState, decreaseState)
.then(function() {
console.log("Execute works");
expect(state.number).toEqual(2);
return cmdHistory.redo(); // can't redo, nothing's undone
})
.then(function() {
console.log("Redo would not work");
expect(state.number).toEqual(2);
return cmdHistory.undo();
})
.then(function() {
console.log("Undo undoes");
expect(state.number).toEqual(1);
return cmdHistory.undo();
})
.then(function() {
console.log("Next undo does nothing");
expect(state.number).toEqual(1);
return cmdHistory.redo(); // but still able to redo
})
.then(function() {
console.log("And redo redoes neatly");
expect(state.number).toEqual(2);
});
$timeout.flush();
}));
This test is dedicated to make sure that commandHistory object works fine, it has to actions: execute
and unExecute
, and three methods: execute
, undo
, redo
, all of which return promises.
Without $timeout.flush()
, all I had in log output was All together
, and no further log messages. Adding $timeout.flush()
has fixed everything up, and now I have all messages shown and all assertions executed
UPDATE
There's another option: you can write your test suite without chaining promises with then
, but simply flushing after each promise has been called, so that to make sure it completes:
it("All usecases 2", inject(function($timeout) {
console.log("All usecases 2");
expect(state.number).toEqual(1);
console.log("Execute works");
cmdHistory.execute(increaseState, decreaseState);
$timeout.flush();
expect(state.number).toEqual(2);
console.log("Redo would not work");
cmdHistory.redo(); // can't redo, nothing's undone
$timeout.verifyNoPendingTasks();
expect(state.number).toEqual(2);
console.log("Undo undoes");
cmdHistory.undo();
$timeout.flush();
expect(state.number).toEqual(1);
console.log("Next undo does nothing");
cmdHistory.undo();
$timeout.verifyNoPendingTasks();
expect(state.number).toEqual(1);
console.log("And redo redoes neatly");
cmdHistory.redo(); // but still able to redo
$timeout.flush();
expect(state.number).toEqual(2);
}));
Please pay attention to the fact in some cases, when my methods like undo
and redo
do not return promise, I call $timeout.verifyNoPendingTasks()
instead of flush
. Which is hard to say if it's good or bad.
Yet in this case test looks more reasonable and much simpler.
$timeout.flush()
, and/ormyPromise.resolve()
in tests to force them to be synchronous. – Michal Charemza$timeout.flush()
works great without the use of Jasmine'sdone
; I don't have a function to demonstrate when I would needdone
+$timeout.flush()
. What if the tests use e.g. an actual$http
backend? It's (obviously) better to mock$http
for speed, but would$timeout.flush()
withoutdone
work there? Does the delayed resolution of the promise make any difference? – Tyler Eich$http
backend, connecting to a real server, then the test would be asynchronous, and you have to usedone
.$timeout.flush()
(or$httpBackend.flush()
) affects the code running locally: you can't call a function to demand that a server respond to the request right now! If you're not sure about how to test a specific function (say, using$timeout
or$http
), then you can post that function in a question). – Michal Charemza