testdouble
Version:
A minimal test double library for TDD with JavaScript
340 lines (294 loc) • 8.55 kB
TypeScript
export as namespace testdouble;
//
// types and interfaces
// ----------------------------------------------------------------------------
export type DoubledObject<T> = T;
export type DoubledObjectWithKey<T extends string> = { [K in T]: any };
export type TestDouble<T> = T;
export type TestDoubleConstructor<T> = Constructor<T>;
interface Call {
context: {};
args: any[];
}
interface Constructor<T> {
new (...args: any[]): T;
}
export interface Captor {
capture(): any;
value?: any;
values?: any[];
}
export interface Explanation {
callCount: number;
calls: Call[];
description: string;
isTestDouble: boolean;
}
export interface MatcherConfig {
matches(matcherArgs: any[], actual: any): boolean;
name?: string | ((matcherArgs: any[]) => string);
onCreate?(matcherInstance: any, matcherArgs: any[]): void;
afterSatisfaction?(matcherArgs: any[], actual: any): void;
}
export interface Matchers {
anything(): any;
isA(type: Function): any;
contains(a: string | any[] | {}): any;
argThat(matcher: Function): any;
not(v: any): any;
captor(): Captor;
create(config: MatcherConfig): any;
}
export const matchers: Matchers;
export interface Stubber<D, R = D extends object ? Partial<D> : D> {
thenReturn<T>(first: R, ...args: Array<R>): TestDouble<T>;
thenDo<T>(f: Function): TestDouble<T>;
thenThrow<T>(first: unknown, ...args: Array<unknown>): TestDouble<T>;
thenResolve<T>(first: R, ...args: Array<R>): TestDouble<T>;
thenReject<T>(first: unknown, ...args: Array<unknown>): TestDouble<T>;
thenCallback<T>(error: any, data: any): TestDouble<T>;
}
export interface PromiseStubber<P, R = P extends object ? Partial<P> : P> {
thenReturn<T>(first: Promise<R>, ...args: Array<Promise<R>>): TestDouble<T>;
thenResolve<T>(first: R, ...args: Array<R>): TestDouble<T>;
thenDo<T>(f: Function): TestDouble<T>;
thenReject<T>(first: unknown, ...args: Array<unknown>): TestDouble<T>;
}
export interface TestdoubleConfig {
promiseConstructor?: any;
ignoreWarnings?: boolean;
suppressErrors?: boolean;
}
export interface VerificationConfig {
ignoreExtraArgs?: boolean;
times?: number;
cloneArgs?: boolean;
}
export interface WhenConfig {
ignoreExtraArgs?: boolean;
times?: number;
cloneArgs?: boolean;
defer?: boolean;
delay?: number;
}
//
// general
// ----------------------------------------------------------------------------
/**
* Update the configuration. Configuration will perist through the lifetime of
* of the entire test. If you need to change a configuration property for a
* single test, you'll need to manage undoing the change yourself (e.g. in
* beforeEach and afterEach hooks).
*
* @export
* @param {TestdoubleConfig} config
*/
export function config(config: TestdoubleConfig): void;
/**
* Reset the state.
*
* @export
*/
export function reset(): void;
/**
* Takes a test double function as an argument and will describe the current
* configuration and state of the test double.
*
* @export
* @template T
* @param {TestDouble<T>} f
* @returns {Explanation}
*/
export function explain<T>(f: TestDouble<T>): Explanation;
//
// fake: constructors
// ----------------------------------------------------------------------------
/**
* Create a fake object constructor the given class.
*
* @export
* @template T
* @param {{ new (...args: any[]): T }} constructor
* @returns {DoubledObject<T>}
*/
export function constructor<T>(
constructor: Constructor<T>
): TestDoubleConstructor<T>;
//
// fake: instance objects
//
/**
* Construct an instance of a faked class.
*
* @export
* @template T
* @param {{ new (...args: any[]): T }} constructor
* @returns {DoubledObject<typeof T>}
*/
export function instance<T>(
constructor: Constructor<T>
): DoubledObject<T>;
//
// fake: functions
// ----------------------------------------------------------------------------
/**
* Create a fake function.
*
* @param {string} [name] Name of function for better messages.
* @returns {TestDouble<Function>}
*/
declare function functionDouble(name?: string): TestDouble<Function>;
/**
* Create a fake function. Typed when type is provided.
* @example td.func<MyType>();
* @template T
* @param {T} [name] Name of function to copy.
* @returns {TestDouble<T>}
*/
declare function functionDouble<T>(name?: T): TestDouble<T>;
export { functionDouble as function };
export { functionDouble as func };
//
// fake: objects
// ----------------------------------------------------------------------------
/**
* Create a fake object that is deep copy of the given object.
*
* @export
* @template T
* @param {{ new (...args: any[]): T }} constructor
* @returns {DoubledObject<T>}
*/
export function object<T>(constructor: Constructor<T>): DoubledObject<T>;
/**
* Create a fake object that has the given list of properties.
*
* @export
* @template Key
* @param {Key[]} props Array of properties.
* @returns {DoubledObjectWithKey<Key>}
*/
export function object<T extends string>(props: T[]): DoubledObjectWithKey<T>;
/**
* Create a fake empty object that is cast as the generic using a Proxy object.
*
* @export
* @template T
* @param {T} object Name of object.
* @returns {DoubledObject<T>}
*/
export function object<T>(object: string): DoubledObject<T>;
/**
* Create a fake empty object using a Proxy object that is cast as the generic of a passed interface.
*
* @export
* @template T
* @returns {DoubledObject<T>}
*/
export function object<T>(): DoubledObject<T>;
/**
* Create a fake object that is deep copy of the given object.
*
* @export
* @template T
* @param {T} object Object to copy.
* @returns {DoubledObject<T>}
*/
export function object<T>(object: T): DoubledObject<T>;
// fake: imitations
/**
* Create a fake object constructor for the given class.
*
* @export
* @template T
* @param {{ new (...args: any[]): T }} constructor
* @param {string} [name]
* @returns {TestDoubleConstructor<T>}
*/
export function imitate<T>(
constructor: Constructor<T>,
name?: string
): TestDoubleConstructor<T>;
/**
* Create a fake object or function.
*
* @export
* @template T
* @param {T} original
* @param {string} [name]
* @returns {TestDouble<T>}
*/
export function imitate<T>(original: T, name?: string): TestDouble<T>;
//
// stubbing
// ----------------------------------------------------------------------------
/**
* Callback marker.
*
* @export
* @param {...any[]} args
*/
export function callback(...args: any[]): void;
/**
* Swap out real dependencies with fake one. Intercept calls to `require`
* that dependency module and ensure your subject is handed a fake instead.
*
* @export
* @param {string} module
* @param {*} [f] replacement
* @returns {*}
*/
export function replace<F>(path: string, f?: F): F;
export function replace(path: string): TestDouble<any>;
/**
* Swap out real dependencies with fake one. Intercept calls to `require`
* that dependency module and ensure your subject is handed a fake instead.
*
* @export
* @param {string} obj
* @param {*} [f]
* @returns {*}
*/
export function replace<T, K extends keyof T>(obj: T, property: K): TestDouble<T[K]>;
export function replace(obj: {}, property: string): TestDouble<any>;
/**
* Swap out real dependencies with fake one. Intercept calls to `import`
* that dependency module and ensure your subject is handed a fake instead.
*
* @export
* @param {string} path
* @param {*} [namedExportStubs]
* @param {*} [defaultExportStub]
* @returns {Promise<{default?: any, [namedExport: string]: any}>}
*/
export function replaceEsm(path: string): Promise<{default?: any, [namedExport: string]: any}>;
export function replaceEsm(path: string, namedExportStubs?: any, defaultExportStub?: any): Promise<void>;
/**
* Swap out real dependencies with fake one. Reference to the property will
* be replace it during your test.
*
* @export
* @param {{}} obj
* @param {string} property
* @param {*} [f]
* @returns {*}
*/
export function replace<F>(obj: {}, property: string, replacement: F): F;
/**
* Stub a specific function call.
*
* @export
* @param {...any[]} args
* @returns {Stubber}
*/
export function when<P>(f: Promise<P>, config?: WhenConfig): PromiseStubber<P>;
export function when<D>(f: D, config?: WhenConfig): Stubber<D>;
/**
* Verify a specific function call to a stubbed function.
*
* @export
* @param {...any[]} args
* @returns {Stubber}
*/
export function verify(a: any, check?: VerificationConfig): void;
export const version: string;