Skip to content

Commit

Permalink
feat: createTestHandler constructor can be extended (#134)
Browse files Browse the repository at this point in the history
- expose createTestHandler
- add constructor parameter to createTestHandler
- write example-readme-custom-testhandler tests
- write documentation about createTestHandler


Closes #133
Ref #134
  • Loading branch information
guillaumearm committed Mar 29, 2018
1 parent 7f98550 commit 088184b
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 13 deletions.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,5 +268,44 @@ testHandler(myHandler())
.run();
```

### Custom testHandler

A custom `testHandler` can be created using `createTestHandler`.

**e.g.**

```js
import { io, createTestHandler } from 'handle-io';


const createCustomTestHandler = (h, mockedIOs, expectedRetValue, assertRet, constructor = createCustomTestHandler) => {
return {
...createTestHandler(h, mockedIOs, expectedRetValue, assertRet, constructor),
matchLog: (arg, ret) => constructor(
h,
[...mockedIOs, [io(console.log)(arg), ret]],
expectedRetValue,
assertRet,
constructor,
),
};
};

const customTestHandler = h => createCustomTestHandler(h);

const log = io(console.log);
const myHandler = handler(function*(value) {
yield log(value);
yield log(value);
return 42;
});

customTestHandler(myHandler('hello world'))
.shouldReturn(42)
.matchLog('hello world')
.matchLog('hello world')
.run()
```
## License
[MIT](https://github.com/guillaumearm/handle-io/blob/master/LICENSE)
128 changes: 128 additions & 0 deletions __bundle_tests__/example-readme-custom-testhandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/* eslint-disable no-console */

import expect from 'expect.js';
import { test, describe } from 'async-describe';

module.exports = ({ io, handler, testHandler, createTestHandler }) => (
describe('[README.md] Custom testHandler example', async () => {
const log = io(console.log);

const createCustomTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = false, constructor = createCustomTestHandler) => {
return {
...createTestHandler(h, mockedIOs, expectedRetValue, assertRet, constructor),
matchLog: (arg, ret) => constructor(
h,
[...mockedIOs, [io(console.log)(arg), ret]],
expectedRetValue,
assertRet,
constructor,
),
};
};
const customTestHandler = h => createCustomTestHandler(h);

const myHandler = handler(function*(value) {
yield log(value);
yield log(value);
return 42;
});

await describe('whith testHandler', async () => {
await test('logs "hello world" twice and return 42', () => {
testHandler(myHandler('hello world'))
.matchIo(log('hello world'))
.matchIo(log('hello world'))
.shouldReturn(42)
.run()
});
});

await describe('with customTestHandler (success)', async () => {
await test('logs "hello world" twice and return 42', async () => {
customTestHandler(myHandler('hello world'))
.matchLog('hello world')
.matchLog('hello world')
.shouldReturn(42)
.run();

customTestHandler(myHandler('hello world'))
.shouldReturn(42)
.matchLog('hello world')
.matchIo(log('hello world'))
.run();

customTestHandler(myHandler('hello world'))
.shouldReturn(42)
.matchIo(log('hello world'))
.matchLog('hello world')
.run();
});
});

await describe('with customTestHandler (failure)', async () => {
await test('throws an error when arguments are invalid', async () => {
expect(() => {
customTestHandler(myHandler('hello world'))
.matchLog('hello world 1')
.matchLog('hello world')
.shouldReturn(42)
.run();
}).to.throwError('Invalid IO#0 function arguments')
expect(() => {
customTestHandler(myHandler('hello world'))
.matchLog('hello world')
.matchLog('hello world 2')
.shouldReturn(42)
.run();
}).to.throwError('Invalid IO#1 function arguments')
});

await test('throws an error when io functions are wrong', async () => {
expect(() => {
customTestHandler(myHandler('hello world'))
.matchIo(io(() => {})('hello world'))
.matchLog('hello world')
.shouldReturn(42)
.run();
}).to.throwError('Invalid IO#0 function');
expect(() => {
customTestHandler(myHandler('hello world'))
.matchLog('hello world')
.matchIo(io(() => {})('hello world'))
.shouldReturn(42)
.run();
}).to.throwError('Invalid IO#1 function');
});

await test('throws an error when returned value is wrong', async () => {
expect(() => {
customTestHandler(myHandler('hello world'))
.matchLog('hello world')
.matchLog('hello world')
.shouldReturn(0)
.run();
}).to.throwError('Invalid returned value');
});

await test('throws an error when too much io ran', async () => {
expect(() => {
customTestHandler(myHandler('hello world'))
.matchLog('hello world')
.shouldReturn(42)
.run();
}).to.throwError('Too much io ran');
});

await test('throws an error when not enough io ran', async () => {
expect(() => {
customTestHandler(myHandler('hello world'))
.matchLog('hello world')
.matchLog('hello world')
.matchLog('hello world')
.shouldReturn(42)
.run();
}).to.throwError('Not enough io ran');
});
});
})
)
1 change: 1 addition & 0 deletions __bundle_tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const testSuites = [
require('./example-readme-addvalues'),
require('./example-readme-promises'),
require('./example-readme-errors'),
require('./example-readme-custom-testhandler'),
];

const bundlePrefix = 'bundle-'
Expand Down
4 changes: 2 additions & 2 deletions __tests__/testHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe('handle-io/testHandler', () => {
.matchIo({ f: f1, args: args2 }, 'b')
.matchIo({ f: f2, args: args1 }, 'c')
.run()
).toThrow('Too much runned io');
).toThrow('Too much io ran');
});

test('not enough io ran', () => {
Expand All @@ -165,7 +165,7 @@ describe('handle-io/testHandler', () => {
.matchIo({ f: f2, args: args2 }, 'd')
.matchIo({ f: () => {}, args: [] }, 'x')
.run()
).toThrow('Not enough runned io');
).toThrow('Not enough io ran');
});

test('invalid io function', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { default as io } from './io';
export { default as handler } from './handler';
export { default as testHandler } from './testHandler';
export { default as testHandler, createTestHandler } from './testHandler';
export { default as catchError } from './catchError';
export { default as simulateThrow } from './simulateThrow';
20 changes: 10 additions & 10 deletions src/testHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,34 @@ import SimulatedThrow from './internal/SimulatedThrow';
import isEqual from 'lodash.isequal';
import { stringify } from './internal/utils';

const createTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = false) => {
export const createTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = false, constructor = createTestHandler) => {
if (typeof h !== 'function') {
throw new Error('Handler should be a function')
}
return {
matchIo: (io, ret) => createTestHandler(
matchIo: (io, ret) => constructor(
h,
[...mockedIOs, [io, ret]],
expectedRetValue,
assertRet
assertRet,
constructor
),
shouldReturn: (expected) => createTestHandler(h, mockedIOs, expected, true),
shouldReturn: (expected) => constructor(h, mockedIOs, expected, true, constructor),
run: () => {
// 1. run handler using a custom runner to get a retValue
// run handler using a custom runner to get a retValue
let mockIndex = 0;
const retValue = h.run((io) => {
if (!mockedIOs[mockIndex]) {
throw new BypassHandlerError('Too much runned io')
throw new BypassHandlerError('Too much io ran');
}
const [expectedIO, mockedRetValue] = mockedIOs[mockIndex];
if (!isEqual(io.f, expectedIO.f)) {
throw new BypassHandlerError(`Invalid IO#${mockIndex} function`)
throw new BypassHandlerError(`Invalid IO#${mockIndex} function`);
}
if (!isEqual(io.args, expectedIO.args)) {
const expectedArgs = stringify(expectedIO.args);
const ioArgs = stringify(io.args);
throw new BypassHandlerError(`Invalid IO#${mockIndex} function arguments: expected \n${expectedArgs}\nbut got \n${ioArgs}`)
throw new BypassHandlerError(`Invalid IO#${mockIndex} function arguments: expected \n${expectedArgs}\nbut got \n${ioArgs}`);
}
mockIndex += 1;
if (mockedRetValue instanceof SimulatedThrow) {
Expand All @@ -39,10 +40,9 @@ const createTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = fals
});

if (mockIndex < mockedIOs.length) {
throw new Error('Not enough runned io');
throw new Error('Not enough io ran');
}

// 3. expectedRetValue and retValue should be equal
if (assertRet && !isEqual(expectedRetValue, retValue)) {
throw new Error(`Invalid returned value : expected ${expectedRetValue} but got ${retValue}`)
}
Expand Down

0 comments on commit 088184b

Please sign in to comment.