npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

ninos

v3.0.0

Published

Simple stubbing/spying for Ava

Downloads

94

Readme

Niños

Simple stubbing/spying for AVA

Example

Setup

const test = require('ninos')(require('ava'));

t.context.stub()

const EventEmitter = require('events');

test('EventEmitter', t => {
  let e = new EventEmitter();
  let s = t.context.stub();
  e.on('event', s);

  e.emit('event');
  t.is(s.calls.length, 1);

  e.emit('event', 'arg');
  t.is(s.calls[1].arguments[0], 'arg');
});

t.context.spy()

const api = require('./api');

test('api.getCurrentUser()', t => {
  let s = t.context.spy(api, 'request', () => {
    return Promise.resolve({ id: 42 });
  });

  await api.getCurrentUser();

  t.deepEqual(s.calls[0].arguments[0], {
    method: 'GET',
    url: '/api/v1/user',
  });
});

Install

yarn add --dev ninos

Usage

ninos()

This method setups the t.context.stub() and t.context.spy() functions. It hooks into AVA to automatically restore spies after each test.

const test = require('ninos')(require('ava'));

t.context.stub()

Call this method to create a function that you can use in place of any other function (as a callback/etc).

test('example', t => {
  let s = t.context.stub(); // [Function]
});

On that function is a calls property which is an array of all the calls you made.

let s = t.context.stub();

s.call('this', 'arg1', 'arg2');

t.deepEqual(s.calls, [
  { this: 'this', arguments: ['arg1', 'arg2'], return: undefined },
]);

You can optional pass an inner function to be called inside the stub to customize its behavior.

let s = t.context.stub((...args) => {
  return 'hello!';
});

s();

t.deepEqual(s.calls, [
  { ..., return: 'hello!' },
]);

If you want to customize the behavior based on the current call you can use s.calls.

let s = t.context.stub((...args) => {
  if (s.calls.length === 0) return 'one';
  if (s.calls.length === 1) return 'two';
  if (s.calls.length === 2) return 'three';
  throw new Error('too many calls!');
});

t.is(s(), 'one');
t.is(s(), 'two');
t.is(s(), 'three');
t.throws(() => s()); // Error: too many calls!

t.context.spy()

If you need to write tests against a method on an object, you should use a spy instead of a stub.

let method = () => 'hello from method';
let object = { method };

let s = t.context.spy(object, 'method');

Just like stubs, spies have a calls property.

let s = t.context.spy(object, 'method');

object.method.call('this', 'arg1', 'arg2');

t.deepEqual(s.calls, [
  { this: 'this', arguments: ['arg1', 'arg2'], return: 'hello from method'; },
]);

By default, spies will call the original function. If you want to customize the behavior you can pass your own inner function.

let s = t.context.spy(object, 'method', (...args) => {
  return 'hello from spy'
});

object.method();

t.deepEqual(s.calls, [
  { ..., return: 'hello from spy' },
]);

If you still want access to the original function you can find it on s.original.

let s = t.context.spy(object, 'method', (...args) => {
  return s.original(...args) + ' and hello from spy';
});

object.method();

t.deepEqual(s.calls, [
  { ..., return: 'hello from method and hello from spy' },
]);

Spies will automatically be restored at the end of your test, but if you want to do it yourself:

let s = test.context.spy(object, 'method');
object.method = s.original;

API

Here is the basic API interface:

type Call =
  | { this: any, arguments: Array<any>, return: any }
  | { this: any, arguments: Array<any>, throw: any }; // when an error was thrown

type Stub = Function & { calls: Array<Call> };
type Spy = Function & { calls: Array<Call>, original: Function };

Design

Niños tries to keep things as miminal as possible. So it avoids APIs like:

let s = t.context.stub();

s.onCall(0).returns('ret1');
s.onCall(1).returns('ret2');

And:

t.toHaveBeenCalledWith(s, 'arg1', 'arg2');

Instead you should write tests like this:

test('example', t => {
  let s = t.context.stub(() => {
    if (s.calls.length === 0) return 'ret1';
    if (s.calls.length === 1) return 'ret2';
  });

  t.deepEqual(s.calls[0], ['arg1', 'arg2']);
});

This is ultimately more flexible and doesn't end up with dozens of weird one-off APIs for you to memorize.

If you prefer the former, Sinon is the library for you.


Note: This is part of a proposal to add stubs/spies to AVA itself