{"id":13526482,"url":"https://github.com/jamiebuilds/ninos","last_synced_at":"2025-03-15T13:31:15.834Z","repository":{"id":65411348,"uuid":"135757739","full_name":"jamiebuilds/ninos","owner":"jamiebuilds","description":"Simple stubbing/spying for AVA","archived":false,"fork":false,"pushed_at":"2020-09-04T02:48:12.000Z","size":204,"stargazers_count":96,"open_issues_count":3,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-12T21:02:48.089Z","etag":null,"topics":["ava","mocks","spies","stubs","testing"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamiebuilds.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-01T19:51:40.000Z","updated_at":"2024-02-25T21:00:29.000Z","dependencies_parsed_at":"2023-01-23T00:16:01.000Z","dependency_job_id":null,"html_url":"https://github.com/jamiebuilds/ninos","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiebuilds%2Fninos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiebuilds%2Fninos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiebuilds%2Fninos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiebuilds%2Fninos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamiebuilds","download_url":"https://codeload.github.com/jamiebuilds/ninos/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243735845,"owners_count":20339537,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ava","mocks","spies","stubs","testing"],"created_at":"2024-08-01T06:01:30.449Z","updated_at":"2025-03-15T13:31:15.507Z","avatar_url":"https://github.com/jamiebuilds.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Niños\n\n\u003e Simple stubbing/spying for [AVA](https://ava.li)\n\n## Example\n\n**Setup**\n\n```js\nconst test = require('ninos')(require('ava'));\n```\n\n**t.context.stub()**\n\n```js\nconst EventEmitter = require('events');\n\ntest('EventEmitter', t =\u003e {\n  let e = new EventEmitter();\n  let s = t.context.stub();\n  e.on('event', s);\n\n  e.emit('event');\n  t.is(s.calls.length, 1);\n\n  e.emit('event', 'arg');\n  t.is(s.calls[1].arguments[0], 'arg');\n});\n```\n\n**t.context.spy()**\n\n```js\nconst api = require('./api');\n\ntest('api.getCurrentUser()', t =\u003e {\n  let s = t.context.spy(api, 'request', () =\u003e {\n    return Promise.resolve({ id: 42 });\n  });\n\n  await api.getCurrentUser();\n\n  t.deepEqual(s.calls[0].arguments[0], {\n    method: 'GET',\n    url: '/api/v1/user',\n  });\n});\n```\n\n## Install\n\n```sh\nyarn add --dev ninos\n```\n\n## Usage\n\n#### `ninos()`\n\nThis method setups the `t.context.stub()` and `t.context.spy()` functions. It\nhooks into AVA to automatically restore spies after each test.\n\n```js\nconst test = require('ninos')(require('ava'));\n```\n\n#### `t.context.stub()`\n\nCall this method to create a function that you can use in place of any other\nfunction (as a callback/etc).\n\n```js\ntest('example', t =\u003e {\n  let s = t.context.stub(); // [Function]\n});\n```\n\nOn that function is a `calls` property which is an array of all the calls you\nmade.\n\n```js\nlet s = t.context.stub();\n\ns.call('this', 'arg1', 'arg2');\n\nt.deepEqual(s.calls, [\n  { this: 'this', arguments: ['arg1', 'arg2'], return: undefined },\n]);\n```\n\nYou can optional pass an inner function to be called inside the stub to\ncustomize its behavior.\n\n```js\nlet s = t.context.stub((...args) =\u003e {\n  return 'hello!';\n});\n\ns();\n\nt.deepEqual(s.calls, [\n  { ..., return: 'hello!' },\n]);\n```\n\nIf you want to customize the behavior based on the current call you can use\n`s.calls`.\n\n```js\nlet s = t.context.stub((...args) =\u003e {\n  if (s.calls.length === 0) return 'one';\n  if (s.calls.length === 1) return 'two';\n  if (s.calls.length === 2) return 'three';\n  throw new Error('too many calls!');\n});\n\nt.is(s(), 'one');\nt.is(s(), 'two');\nt.is(s(), 'three');\nt.throws(() =\u003e s()); // Error: too many calls!\n```\n\n#### `t.context.spy()`\n\nIf you need to write tests against a method on an object, you should use a spy\ninstead of a stub.\n\n```js\nlet method = () =\u003e 'hello from method';\nlet object = { method };\n\nlet s = t.context.spy(object, 'method');\n```\n\nJust like stubs, spies have a `calls` property.\n\n```js\nlet s = t.context.spy(object, 'method');\n\nobject.method.call('this', 'arg1', 'arg2');\n\nt.deepEqual(s.calls, [\n  { this: 'this', arguments: ['arg1', 'arg2'], return: 'hello from method'; },\n]);\n```\n\nBy default, spies will call the original function. If you want to customize the\nbehavior you can pass your own inner function.\n\n```js\nlet s = t.context.spy(object, 'method', (...args) =\u003e {\n  return 'hello from spy'\n});\n\nobject.method();\n\nt.deepEqual(s.calls, [\n  { ..., return: 'hello from spy' },\n]);\n```\n\nIf you still want access to the original function you can find it on\n`s.original`.\n\n```js\nlet s = t.context.spy(object, 'method', (...args) =\u003e {\n  return s.original(...args) + ' and hello from spy';\n});\n\nobject.method();\n\nt.deepEqual(s.calls, [\n  { ..., return: 'hello from method and hello from spy' },\n]);\n```\n\nSpies will automatically be restored at the end of your test, but if you want\nto do it yourself:\n\n```js\nlet s = test.context.spy(object, 'method');\nobject.method = s.original;\n```\n\n## API\n\nHere is the basic API interface:\n\n```ts\ntype Call =\n  | { this: any, arguments: Array\u003cany\u003e, return: any }\n  | { this: any, arguments: Array\u003cany\u003e, throw: any }; // when an error was thrown\n\ntype Stub = Function \u0026 { calls: Array\u003cCall\u003e };\ntype Spy = Function \u0026 { calls: Array\u003cCall\u003e, original: Function };\n```\n\n## Design\n\nNiños tries to keep things as miminal as possible. So it avoids APIs like:\n\n```js\nlet s = t.context.stub();\n\ns.onCall(0).returns('ret1');\ns.onCall(1).returns('ret2');\n```\n\nAnd:\n\n```js\nt.toHaveBeenCalledWith(s, 'arg1', 'arg2');\n```\n\nInstead you should write tests like this:\n\n```js\ntest('example', t =\u003e {\n  let s = t.context.stub(() =\u003e {\n    if (s.calls.length === 0) return 'ret1';\n    if (s.calls.length === 1) return 'ret2';\n  });\n\n  t.deepEqual(s.calls[0], ['arg1', 'arg2']);\n});\n```\n\nThis is ultimately more flexible and doesn't end up with dozens of weird\none-off APIs for you to memorize.\n\nIf you prefer the former, Sinon is the library for you.\n\n---\n\n\u003e **Note:** This is part of a [proposal to add stubs/spies to AVA itself](https://github.com/avajs/ava/issues/1825)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamiebuilds%2Fninos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamiebuilds%2Fninos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamiebuilds%2Fninos/lists"}