All Projects → jamiebuilds → Ninos

jamiebuilds / Ninos

Simple stubbing/spying for AVA

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Ninos

get
🚚 A really small and type-safe (requires TypeScript >= 4.1.3) function, that gets a nested value from an object using a path string (like "a.b[0].d"). If value is 'undefined' or unreachable returns the placeholder instead.
Stars: ✭ 13 (-86.87%)
Mutual labels:  ava
Express Rest Api Boilerplate
Express REST API with JWT Authentication and support for sqlite, mysql, and postgresql
Stars: ✭ 384 (+287.88%)
Mutual labels:  ava
Create Ava
Add AVA to your project
Stars: ✭ 14 (-85.86%)
Mutual labels:  ava
awesome-javascript-testing
🔧 Awesome JavaScript testing resources
Stars: ✭ 28 (-71.72%)
Mutual labels:  ava
Ava
Node.js test runner that lets you develop with confidence 🚀
Stars: ✭ 19,458 (+19554.55%)
Mutual labels:  ava
Istanbuljs
monorepo containing the various nuts and bolts that facilitate istanbul.js test instrumentation
Stars: ✭ 656 (+562.63%)
Mutual labels:  ava
eslint-config-adjunct
A reasonable collection of plugins to use alongside your main esLint configuration
Stars: ✭ 39 (-60.61%)
Mutual labels:  ava
Suman
🌇 🌆 🌉 Advanced, user-friendly, language-agnostic, super-high-performance test runner. http://sumanjs.org
Stars: ✭ 57 (-42.42%)
Mutual labels:  ava
Enzyme
JavaScript Testing utilities for React
Stars: ✭ 19,781 (+19880.81%)
Mutual labels:  ava
Jest Codemods
Codemods for migrating to Jest https://github.com/facebook/jest 👾
Stars: ✭ 731 (+638.38%)
Mutual labels:  ava
Hare
🐇 Application boilerplate based on Vue.js 2.x, Koa 2.x, Element-UI and Nuxt.js
Stars: ✭ 258 (+160.61%)
Mutual labels:  ava
Awesome Ava
Awesome AVA resources
Stars: ✭ 322 (+225.25%)
Mutual labels:  ava
Public
Repository for wallaby.js questions and issues
Stars: ✭ 662 (+568.69%)
Mutual labels:  ava
knack
A streamlined wrapper around node-rdkafka made with independent composable parts.
Stars: ✭ 15 (-84.85%)
Mutual labels:  ava
Sublime Ava
Snippets for AVA
Stars: ✭ 41 (-58.59%)
Mutual labels:  ava
persistence
💾 Persistence provides a pretty easy API to handle Storage's implementations.
Stars: ✭ 18 (-81.82%)
Mutual labels:  ava
Ava Docs
Localized docs for AVA
Stars: ✭ 455 (+359.6%)
Mutual labels:  ava
Atom Ava
Snippets for AVA and run tests directly in the editor
Stars: ✭ 96 (-3.03%)
Mutual labels:  ava
Mongomem
In-memory MongoDB Server. Ideal for testing.
Stars: ✭ 51 (-48.48%)
Mutual labels:  ava
Mmaction2
OpenMMLab's Next Generation Video Understanding Toolbox and Benchmark
Stars: ✭ 684 (+590.91%)
Mutual labels:  ava

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].