What's this?
This is yet another synchronous implementation od JavaScript Promise, written to ease the pain of unit testing. It's written in TypeScript as a support class for an Jest Mock Axios.
Why yet another?
Because of "code first, search later" approach to solving problems.
What's in this document?
How does it work?
Well' it works the same way a normal Promise would work, with the exception that does it right away and not at later time.
Let's use the following example to see how a synchronous promise is different from a regular one:
let resolveFn;
let promise = new Promise((resolve, reject) => {
resolveFn = resolve;
console.log('1. Promise is ready');
});
promise.then(() => console.log('2. Promise is resolved'));
console.log('3. Will resolve the promise');
resolveFn();
console.log('4. Last line of code');
The regular promise would produce the following console output:
1. Promise is ready
3. Will resolve the promise
4. Last line of code
2. Promise is resolved
The "2. Promise is resolved" is logged to the console before "4. Last line of code" because regular promise runs registered hander functions (then
, catch
) in the next JavaScript timeslot, after all the code has finished executing.
Let's now have a look at what the console output will look like for synchronous promise:
1. Promise is ready
3. Will resolve the promise
2. Promise is resolved
4. Last line of code
As you can see, the then
handler was executed before the last line of code!
If you've ever tried unit-testing async code, than you'll know how painfull that can be!
How to use it?
Synchronous Promise is usefull for testing an async component.
We'll look at an example of testing a method, which multiplies two numbers provided as a promise payload, after which it passes the result by calling a callback function. Here's the code of the component we want to test:
import Promise from 'es6-promise';
const onPromiseMultiply = (promise, callback) => {
promise.then((a,b) => {
callback(a*b);
})
};
export {onPromiseMultiply};
Testing in the async way
To test this method in a regular Jest test, we would need to write something along these lines:
import {onPromiseMultiply} from '../component.js';
describe('testing the multiply component', () => {
it('should multiply two numbers and provide the result to the callback function', () => {
let callbackFn = jest.fn();
let promise = new Promise((resolve, reject) {
resolve(1,2);
});
onPromiseMultiply(promise, callbackFn);
return(promise.then(() => {
expect(callbackFn).toHaveBeenCalledWith(3);
}));
});
})
As we can see, we need to have a hard look at the code to see the order in which our code gets executed. Can we make this better? Yes we can! In the following section we'll see how ...
Applying the synchronous Promise
The first thing we need to do is install this component: npm i --save-dev jest-mock-promise
Since our component uses es6-promise
, we'll manually mock this dependency (if you don't know what manual mocking is, have a look at Manual Mocks @ Jest ). We'll create a __mocks__
directory inside our project root. There we'll create a es6-promise.js
file with the following content:
import JestMockPromise from './jest-mock-promise';
export { JestMockPromise as Promise };
Now that's set up, we can modify our test:
import {onPromiseMultiply} from '../component.js';
describe('testing the multiply component', () => {
it('should multiply two numbers and provide the result to the callback function', () => {
let callbackFn = jest.fn();
let promise = new Promise();
onPromiseMultiply(promise, callbackFn);
promise.resolve(1,2);
expect(callbackFn).toHaveBeenCalledWith(3);
});
})
As we can see, there's no problem in reading this test! Hooray!
API
The API of this synchronous promise matches the one of the regular Promise, with two additional instance methods (attached to an instance of the Promise):
resolve
- resolves a promise instancereject
- rejects a promise instance
This methods do the same job as the ones passed to the main callback function:
new Promise((resolve, reject) => { resolve(1,2); });
Having them attached to the instance enables us to call them outside the callback function, which makes our code much more readable:
let promise = new Promise();
promise.resolve(1,2);
License
MIT License, http://www.opensource.org/licenses/MIT