From bd4a14159dd151be71492591f2f9f00cc8c37fa4 Mon Sep 17 00:00:00 2001 From: johanblumenberg Date: Thu, 23 May 2019 23:12:34 +0200 Subject: [PATCH] Add defer() for testing deferred promise resolution --- README.md | 11 +++++++++++ src/ts-mockito.ts | 17 +++++++++++++++++ test/defer.spec.ts | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 test/defer.spec.ts 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); + }); +});