fake-promise
v2.5.44
Published
Gives full control over when ES6 promises are resolved by providing an implementation of Promise with behavior controlled by .resolve(result) and .reject(error) methods. Intended for use in unit tests.
Downloads
2,177
Maintainers
Readme
FakePromise :pray:
Total control over WHEN ES6 promises are resolved or rejected.
Why would I want it?
- 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.
Installation
npm install --save-dev fake-promise
Loading
// node-style require
const { FakePromise } = require('fake-promise');
// es6-style default import
import FakePromise from 'fake-promise';
Documentation
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;
}
(Not So) Minimal Example
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),
);
});
});
});
Debugging Techniques
Printing Stack Traces
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);
Using .toString()
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);
License
Copyright © 2017 - 2019 Maciej Chałapuk. Released under MIT license.