diff --git a/README.md b/README.md index 797bc62..c7122fd 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,17 @@ when(mockedFoo.fetchData("a")).thenResolve({id: "a", value: "Hello world"}); when(mockedFoo.fetchData("b")).thenReject(new Error("b does not exist")); ``` +### Defer resolving promises + +The actions `.thenResolve()` and `.thenReject()` are returning promises that are already resolved or rejected. Sometimes you want to control the order or timing of when promises are resolved. In that case it is useful to return a deferred promise, and resolve it from the test code, when appropriate. + +```typescript +let d = defer(); +when(obj.method()).thenReturn(d); // Return a promise that is not resolved yet + +d.resolve(1); // Later, the promise is resolved or rejected +``` + ### Resetting mock calls You can reset just mock call counter diff --git a/src/ts-mockito.ts b/src/ts-mockito.ts index 7f2b162..02aef49 100644 --- a/src/ts-mockito.ts +++ b/src/ts-mockito.ts @@ -123,6 +123,22 @@ export function objectContaining(expectedValue: Object): any { return new ObjectContainingMatcher(expectedValue) as any; } +export type Deferred = Promise & { + resolve: (value: T) => void; + reject: (err: any) => void; +}; + +export function defer(): Deferred { + let resolve: (value: T) => void; + let reject: (err: any) => void; + + const d = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return Object.assign(d, { resolve, reject }); +} + // Export default object with all members (ember-browserify doesn't support named exports). export default { spy, @@ -144,4 +160,5 @@ export default { strictEqual, match, objectContaining, + defer, }; diff --git a/test/defer.spec.ts b/test/defer.spec.ts new file mode 100644 index 0000000..159fc2d --- /dev/null +++ b/test/defer.spec.ts @@ -0,0 +1,35 @@ +import { anything, defer, fnmock, instance, mock, verify, when } from "../src/ts-mockito"; + +class MockObject { + public method(): Promise { + return Promise.resolve(100); + } +} + +describe("defer", () => { + it("resolves a promise", async () => { + const resolved: (value: number) => void = fnmock(); + const obj: MockObject = mock(MockObject); + const d = defer(); + when(obj.method()).thenReturn(d); + + instance(obj).method().then(instance(resolved)); + verify(resolved(anything())).never(); + + d.resolve(1); + await verify(resolved(1)).timeout(100); + }); + + it("rejects a promise", async () => { + const rejected: (err: any) => void = fnmock(); + const obj: MockObject = mock(MockObject); + const d = defer(); + when(obj.method()).thenReturn(d); + + instance(obj).method().catch(instance(rejected)); + verify(rejected(anything())).never(); + + d.reject(1); + await verify(rejected(1)).timeout(100); + }); +});