Skip to content

tinylibs/tinyspy

Repository files navigation

tinyspy

minimal fork of nanospy, with more features 🕵🏻‍♂️

A 10KB package for minimal and easy testing with no dependencies. This package was created for having a tiny spy library to use in vitest, but it can also be used in jest and other test environments.

In case you need more tiny libraries like tinypool or tinyspy, please consider submitting an RFC

Installing

// with npm
$ npm install -D tinyspy

// with pnpm
$ pnpm install -D tinyspy

// with yarn
$ yarn install -D tinyspy

Usage

spy

Simplest usage would be:

const fn = (n) => n + '!'
const spied = spy(fn)

spied('a')

console.log(spied.called) // true
console.log(spied.callCount) // 1
console.log(spied.calls) // [['a']]
console.log(spied.results) // [['ok', 'a!']]
console.log(spied.returns) // ['a!']

You can reset calls, returns, called and callCount with reset function:

const spied = spy((n) => n + '!')

spied('a')

console.log(spied.called) // true
console.log(spied.callCount) // 1
console.log(spied.calls) // [['a']]
console.log(spied.returns) // ['a!']

spied.reset()

console.log(spied.called) // false
console.log(spied.callCount) // 0
console.log(spied.calls) // []
console.log(spied.returns) // []

Since 3.0, tinyspy doesn't unwrap the Promise in returns and results anymore, so you need to await it manually:

const spied = spy(async (n) => n + '!')

const promise = spied('a')

console.log(spied.called) // true
console.log(spied.results) // ['ok', Promise<'a!'>]

await promise

console.log(spied.results) // ['ok', Promise<'a!'>]

console.log(await spied.returns[0]) // 'a!'

Warning

This also means the function that returned a Promise will always have result type 'ok' even if the Promise rejected

Tinyspy 3.0 still exposes resolved values on resolves property:

const spied = spy(async (n) => n + '!')

const promise = spied('a')

console.log(spied.called) // true
console.log(spied.resolves) // [] <- not resolved yet

await promise

console.log(spied.resolves) // ['ok', 'a!']

spyOn

All spy methods are available on spyOn.

You can spy on an object's method or setter/getter with spyOn function.

let apples = 0
const obj = {
  getApples: () => 13,
}

const spy = spyOn(obj, 'getApples', () => apples)
apples = 1

console.log(obj.getApples()) // prints 1

console.log(spy.called) // true
console.log(spy.returns) // [1]
let apples = 0
let fakedApples = 0
const obj = {
  get apples() {
    return apples
  },
  set apples(count) {
    apples = count
  },
}

const spyGetter = spyOn(obj, { getter: 'apples' }, () => fakedApples)
const spySetter = spyOn(obj, { setter: 'apples' }, (count) => {
  fakedApples = count
})

obj.apples = 1

console.log(spySetter.called) // true
console.log(spySetter.calls) // [[1]]

console.log(obj.apples) // 1
console.log(fakedApples) // 1
console.log(apples) // 0

console.log(spyGetter.called) // true
console.log(spyGetter.returns) // [1]

You can reassign mocked function and restore mock to its original implementation with restore method:

const obj = {
  fn: (n) => n + '!',
}
const spied = spyOn(obj, 'fn').willCall((n) => n + '.')

obj.fn('a')

console.log(spied.returns) // ['a.']

spied.restore()

obj.fn('a')

console.log(spied.returns) // ['a!']

You can even make an attribute into a dynamic getter!

let apples = 0
const obj = {
  apples: 13,
}

const spy = spyOn(obj, { getter: 'apples' }, () => apples)

apples = 1

console.log(obj.apples) // prints 1

You can restore spied function to its original value with restore method:

let apples = 0
const obj = {
  getApples: () => 13,
}

const spy = spyOn(obj, 'getApples', () => apples)

console.log(obj.getApples()) // 0

obj.restore()

console.log(obj.getApples()) // 13

Authors


Mohammad Bagher

Vladimir

Sponsors

Your sponsorship can make a huge difference in continuing our work in open source!

Vladimir sponsors

Mohammad sponsors