Total control over WHEN ES6 promises are resolved or rejected.
- FakePromise is a single-class library without any run-time dependencies,
- It provides a fully functional implementation of Promise with additional testing utilities,
- Fine grained control of resolution of each promise in a chain (
.then(...).then(...)
), - Thoroughly unit-tested and field-tested in several commercial projects,
- Usable in modern JavaScript, TypeScript and CoffeeScript.
This package is intended for use in unit tests. For production, please use Bluebird.
npm install --save-dev fake-promise
// node-style require
const { FakePromise } = require('fake-promise');
// es6-style default import
import FakePromise from 'fake-promise';
For full documentation, please see JSDoc comments in FakePromise class.
/**
* TL; DR
*/
export class FakePromise<T> implements Promise<T> {
// create already resolved/rejected instances of FakePromise (will resolve whole chain)
static resolve<U = void>(result ?: U | Promise<U>) : FakePromise<U>;
static reject<U = void>(error : any) : FakePromise<U>;
// no-arg constructor (without required executor as in standard promise)
constructor() {}
// promise specification methods (standard ES promise interface)
then<U>(onresolved : Callback<U>, onrejected ?: Callback<any>) : Promise<U>;
catch(onrejected : Callback<any>) : Promise<any>;
finally(onfulfilled : Callback<void>) : Promise<void>;
// resolve/reject full promise chain
resolve(result ?: T | Promise<T>) : void;
reject(error ?: any) : void;
// resolve/reject single promise in a chain and return next promise from the chain
resolveOne<U = void>(result ?: T | Promise<T>) : FakePromise<U>;
rejectOne<U = void>(error ?: any) : FakePromise<U>;
// set result/error of a promise without resolving/rejecting
// (to call resolve without arguments afterwards)
setResult(result : T | Promise<T>) : void;
setError(error : any) : void;
}
import FakePromise from 'fake-promise';
/**
* Async function to be tested.
*
* It calls `asyncFunctionDependency`, waits for it's promise
* to be resolved, and returns the result of resolved promise.
*/
async function testedFunction(asyncFunctionDependency) {
try {
const result = await asyncFunctionDependency();
return result;
} catch (e) {
throw e;
}
}
/**
* Tests of `testedFunction` using FakePromise.
*/
describe('testedFunction(asyncDependency)', () => {
let dependencyPromise;
let asyncDependency;
let resultPromise;
beforeEach(() => {
// program `asyncDependency` to return a fresh instance of FakePromise
dependencyPromise = new FakePromise();
asyncDependency = sinon.stub().returns(dependencyPromise);
resultPromise = testedFunction(asyncDependency);
// At this point `dependencyPromise` is not yet resolved,
// so `resultPromise` isn't also.
});
describe('when after resolving dependency promise', () => {
const expectedResult = "result";
beforeEach(end => {
// could be also .resolveOne
dependencyPromise.resolve(expectedResult);
// At this point `dependencyPromise` is resolved, `resultPromise` is not.
// `setImmediate` is needed in order to wait single tick
// for resolution of implicit promise created by `await` keyword.
// `resultPromise` will be resolved before `end` is called.
setImmediate(end);
});
it('resolves result promise', () => {
// Returning promise so that the test will fail if promise is rejected.
return resultPromise.then(result => result.should.eql(expectedResult));
});
});
describe('when after rejecting dependency promise', () => {
const expectedError = new Error("fail");
beforeEach(end => {
// could be also .rejectOne
dependencyPromise.reject(expectedError);
// At this point `dependencyPromise` is rejected, `resultPromise` is not.
// `setImmediate` is needed in order to wait single tick
// for rejection of implicit promise created by `await` keyword.
// `resultPromise` will be rejected before `end` is called.
setImmediate(end);
});
it('rejects result promise', () => {
// Testing rejection is tricky as both resolution and rejection cases
// must be tested in callbacks of the same promise instance
// (in single call to `.then(onResolved, onRejected)`).
return resultPromise.then(
result => { throw new Error(`expected rejection, got result: ${result}`) },
err => err.should.eql(expectedError),
);
});
});
});
FakePromise stores stack traces of all promise specifications, result
provisions and promise resolutions. When debugging problems with tests,
it may be helpful to console.log
one or more of stored traces.
// print stack trace of call to `.then(...)` or `.catch`
console.log(fakePromise.specifyTrace);
// print stack trace of call to `.resove(...)`
console.log(fakePromise.resolveTrace);
// print stack trace of call to `.reject(...)`
console.log(fakePromise.rejectTrace);
// print stack trace of calls to `.setResult(...)`
console.log(fakePromise.resultTrace);
// print stack trace of calls to `.setResult(Promise)`
console.log(fakePromise.promiseTrace);
// print stack trace of calls to `.setError(...)`
console.log(fakePromise.errorTrace);
FakePromise implements .toString()
method which provides information about
internal state of FakePromise instance. When encountering problems with
debugging async code, printing used instance of FakePromise may prove helpful.
console.log(fakePromise);
Copyright © 2017 - 2019 Maciej Chałapuk. Released under MIT license.