Skip to content

stub

Build a test double from method names, an object, or a class.

Signatures

typescript
stub<I>(cls: new (...args: never[]) => I): Wrapped<I>
stub<T>(obj: T): Wrapped<T>
stub<T>(methodNames: (keyof T)[]): Wrapped<T>
stub<T>(
  methodNames: string[],
  properties?: { name: PropertyKey; options: PropertyDescriptor }[],
  options?: StubOptions
): Wrapped<T>

Parameters

target (first arg)

Can be:

  • Array of method names — TypeScript inference drives typing via stub<T>([...]).
  • Existing object — all own + inherited function-typed keys become stubbed methods.
  • Class constructor — walks the prototype chain (not statics, unless { static: true }).

properties (optional second arg)

Array of { name, options } descriptors to attach non-method fields. Useful for interfaces that mix methods and data:

typescript
stub<Person & { age: number }>(
  ['greet'],
  [{ name: 'age', options: { value: 25, enumerable: true } }]
)

options (optional third arg)

typescript
interface StubOptions extends Options {
  static?: boolean                  // include static methods when stubbing a class
  debug: {
    prefix: string | undefined      // defaults to 'deride'
    suffix: string | undefined      // defaults to 'stub'
  }
}

debug.prefix and debug.suffix namespace the debug logger for this mock — useful when multiple mocks exist and you want to filter per-mock logs.

Returns

A Wrapped<T> — your original type augmented with setup, expect, spy, called, snapshot, restore, and EventEmitter methods. See Types.

Examples

From method names

typescript
interface Database {
  query(sql: string): Promise<unknown[]>
}
const mock = stub<Database>(['query'])
mock.setup.query.toResolveWith([])

From an existing instance

typescript
const real = { greet: (n: string) => `hi ${n}` }
const mock = stub(real)
mock.setup.greet.toReturn('mocked')
mock.greet('x')   // 'mocked'

From a class

typescript
class Greeter {
  greet(name: string) { return `hi ${name}` }
  static version() { return '1.0' }
}

const mock = stub(Greeter)                        // prototype methods only
const staticMock = stub<typeof Greeter>(Greeter, undefined, {
  debug: { prefix: 'deride', suffix: 'stub' },
  static: true,                                    // statics only
})

With property descriptors

typescript
const bob = stub<Person & { age: number }>(
  ['greet'],
  [{ name: 'age', options: { value: 25, enumerable: true, writable: false } }]
)
bob.age   // 25

stub.class — constructor mocking

typescript
stub.class<C extends new (...args: any[]) => object>(
  ctor?: C,
  opts?: { methods?: string[]; static?: boolean }
): MockedClass<C>

Returns a new-able proxy that:

  • Records constructor calls (MockedClass.expect.constructor.*)
  • Produces a fresh Wrapped<InstanceType<C>> per new call
  • Maintains an instances[] array of every constructed mock
  • Provides setupAll(fn) to apply setups across existing and future instances

Example

typescript
class Database {
  constructor(public conn: string) {}
  async query(sql: string): Promise<unknown[]> { return [] }
}

const MockedDb = stub.class(Database)
MockedDb.setupAll(inst => inst.setup.query.toResolveWith([]))

const a = new MockedDb('conn-a')
const b = new MockedDb('conn-b')

MockedDb.expect.constructor.called.twice()
MockedDb.instances.length   // 2
await a.query('x')          // []

Signatures on the returned MockedClass

typescript
interface MockedClass<C> {
  new (...args: ConstructorParameters<C>): Wrapped<InstanceType<C>>
  readonly expect: { constructor: MockExpect }
  readonly spy:    { constructor: MethodSpy }
  readonly instances: readonly Wrapped<InstanceType<C>>[]
  setupAll(fn: (instance: Wrapped<InstanceType<C>>) => void): void
}

See also

Released under the MIT License.