I've recently encountered a strange behaviour. I have a big project that contains a lot of the tests and some of them were written in a synchronous way, assuming that the promise library is the one without deferred
.
However, after preparing the environment on my machine (Mac OS X, nodeJS 0.12.18 - i know :( ), the tests seem to run with a different promise implementation - this time using async version with deferred and hence following test fails:
// Some generic mocking code here
instance.doSomething(); // This returns a promise
// Immediately after the previous call, we check the results and clean mocks
sinon.assert.called(request.Request);
request.Request.restore();
It started to work after being rewritten like this:
return instance.doSomething().then(function() {
sinon.assert.called(request.Request);
request.Request.restore();
});
To sum up, instance.doSomething
performs two requests.
If the promise is called synchronously, request
mock gets restored after both calls. If the promise is called asynchronously, first call succeeds but the second one fails, as in the meantime the stub was restored (before second call).
My questions are:
All of this seems really strange, especially as the code uses bluebird
as the main Promise library...
If you what you are testing won't be guaranteed to be in the correct state for testing until the promise is resolved, then you should write your test as you show in your 2nd snippet. That is the correct way to test conditions that depend on resolved promises. The fact that your initial code worked is due to luck. Consider the following code:
const assert = require("assert");
const Promise = require("bluebird");
let moo = "initial";
function someFunc() {
return Promise.resolve()
.then(function () {
moo = "modified";
});
}
beforeEach(() => moo = "initial");
it("test one", () => {
someFunc();
assert.equal(moo, "modified");
});
it("test two", () => {
return someFunc().then(() => {
assert.equal(moo, "modified");
});
});
The promise in someFunc
is resolved immediately, but it does not matter. test one
fails because I'm not waiting for the promise. It does not matter if I use Bluebird or Node's stock Promise
implementation.
There may be circumstances under which test one
will pass, but that's just luck because promises do not guarantee that it will work. This luck may change, if:
You switch to a different promise implementation.
You run on a different platform. Promise implementations have to work with that the various platforms give them. And may thus behave somewhat differently from platform to platform, which is fine, so long as it does not violate the specs. However, the behavior your initial code depended on is not guaranteed by the specs so it may not be maintained on all platforms.
A new version of the promise implementation you use is released and no longer maintains the behavior you were relying on.
Is is possible that on my machine and CI, Mocha uses different promise implementations?
Looking at Mocha's code I do not see any location where Mocha instantiate promises. It detects whether it
returns a promise and depends on the API that promises provide but it does not create its own promises.
Is there a way to force promise implementation for Mocha?
See above. It receives the promises you return so it uses whatever implementation you use in your test suite.
Maybe the changed Promise comes from another place in the code?
Unsure what you mean there.