diff --git a/README.md b/README.md index 797bc62..471baa2 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,18 @@ Syntax is the same as with getter values. Please note, that stubbing properties that don't have getters only works if [Proxy](http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-objects) object is available (ES6). +### Mocking free functions + +Sometimes you need to mock a function, not an object, for example to pass as a callback somewhere. This can be done using `fnmock()`. It works just like any other mock, except it's a function, not an object. + +```typescript +let fn: (a: number, b: string) => number = fnmock(); +when(fn(10, 'hello')).thenReturn(5); + +instance(fn)(10, 'hello'); // returns 5 +verify(fn(10, 'hello')).called(); +``` + ### Call count verification ``` typescript diff --git a/package.json b/package.json index 051a227..db07354 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "ts-helpers": "^1.1.2", "ts-jest": "^23.0.1", "tslint": "^5.7.0", - "typescript": "^2.7.2" + "typescript": "^3.4.5" }, "dependencies": { "lodash": "^4.17.5" diff --git a/src/Mock.ts b/src/Mock.ts index dc2b53b..2dd7fd9 100644 --- a/src/Mock.ts +++ b/src/Mock.ts @@ -175,7 +175,7 @@ export class Mocker { } private createMethodToStub(key: string): () => any { - return (...args) => { + return (...args: any[]) => { if (!this.methodStubCollections[key]) { this.methodStubCollections[key] = new MethodStubCollection(); } diff --git a/src/ts-mockito.ts b/src/ts-mockito.ts index 7f2b162..2c097c1 100644 --- a/src/ts-mockito.ts +++ b/src/ts-mockito.ts @@ -36,6 +36,17 @@ export function mock(clazz: (new(...args: any[]) => T) | (Function & { protot return new Mocker(clazz).getMock(); } +export function fnmock(): (...args: T) => R { + class Mock { + public fn(...args: T): R { return null as R; } + } + + const m: Mock = mock(Mock); + (m.fn as any).__tsmockitoInstance = instance(m).fn; + (m.fn as any).__tsmockitoMocker = (m as any).__tsmockitoMocker; + return m.fn; +} + export function verify(method: T): MethodStubVerificator { return new MethodStubVerificator(method as any); } @@ -127,6 +138,7 @@ export function objectContaining(expectedValue: Object): any { export default { spy, mock, + fnmock, verify, when, instance, diff --git a/test/mocking.functions.spec.ts b/test/mocking.functions.spec.ts new file mode 100644 index 0000000..f32d060 --- /dev/null +++ b/test/mocking.functions.spec.ts @@ -0,0 +1,51 @@ +import { capture, fnmock, instance, reset, resetCalls, verify, when } from "../src/ts-mockito"; + +describe("mocking", () => { + describe("mocking functions", () => { + it("should mock free functions", () => { + const fn: () => number = fnmock(); + + when(fn()).thenReturn(1); + + expect(instance(fn)()).toEqual(1); + verify(fn()).called(); + }); + + it("should match arguments of free functions", () => { + const fn: (a: string, b: number) => number = fnmock(); + + when(fn("a", 1)).thenReturn(1); + + expect(instance(fn)("a", 1)).toEqual(1); + expect(instance(fn)("a", 2)).toBeNull(); + verify(fn("a", 1)).called(); + }); + + it("should reset mocks", () => { + const fn: () => number = fnmock(); + + when(fn()).thenReturn(1); + expect(instance(fn)()).toEqual(1); + + reset(fn); + expect(instance(fn)()).toBeNull(); + }); + + it("should reset calls", () => { + const fn: () => number = fnmock(); + + instance(fn)(); + verify(fn()).once(); + + resetCalls(fn); + verify(fn()).never(); + }); + + it("should capture parameters", () => { + const fn: (a: string) => void = fnmock(); + + instance(fn)("a"); + expect(capture(fn).last()).toEqual(["a"]); + }); + }); +});