/tinyspy

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

Primary LanguageTypeScript

tinyspy

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

A 7KB 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.

Installing

// with npm
npm install -D tinyspy

// with pnpm
pnpm install -D tinyspy

// with yarn
yarn install -D tinyspy

Usage

Warning! Does not support ESM mocking. You can use tinyspy with vitest, who performs additional transformations to make ESM mocking work.

spy

Simplest usage would be:

const fn = (n: string) => 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: string) => 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) // []

If you have async implementation, you need to await the method to get awaited results (if you don't, you will get a Promise inside results):

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

const promise = spied('a')

console.log(spied.called) // true
console.log(spiet.returns) // [Promise]

await promise

console.log(spiet.returns) // ['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 it's original implementation with restore method:

const obj = {
  fn: (n: string) => 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 it's 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