Code Monkey home page Code Monkey logo

ninos's Introduction

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

ninos's People

Contributors

chocpanda avatar delucis avatar jamiebuilds avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.