Giter VIP home page Giter VIP logo

fake-promise's Introduction

FakePromise 🙏

Current Version Build Status Dependencies DevDependencies

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.

fake-promise's People

Contributors

mchalapuk avatar

Stargazers

 avatar  avatar

Watchers

 avatar

fake-promise's Issues

Does not support multiple then() calls

Multiple then() calls on the same promise (not chained) fail with an error:

const promise = new FakePromise<void>()
promise.then(() => console.log('first then'))
promise.then(() => console.log('second then'))
promise.resolve()
    FakePromiseError: promise already specified
        CURRENT STATE:
          FakePromise#4 {
            specified: true,
          }
        STATE AFTER SPECIFICATION:
          FakePromise#4 {
            specified: true,
          }
        STACKTRACE OF SPECIFICATION:
        ...

Real promises support multiple then() calls and it would be helpful if FakePromise would support it too in case the tested code uses this behavior.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.