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 🙏

© 2024 – Pkg Stats / Ryan Hefner

gen-tester

v4.0.4

Published

Test generators with ease

Downloads

465

Readme

gen-tester Build Status

Test generators with ease

Why?

Testing generators is kind of a pain to do manually. Because of the way generators work, the order in which mock values are injected into a generator relates to the previous yield.

How?

const { call } = require('cosed'); // side-effects as data library
const test = require('tape');
const { genTester, yields } = require('gen-tester');

function* genCall() {
  const resp = yield call(fetch, 'http://httpbin.org/get');
  const data = yield call([resp, 'json']);
  return { ...data, extra: 'stuff' };
}

test('genCall', (t) => {
  t.plan(1);

  const respValue = { resp: 'value', json: 'hi' };
  const returnValue = { data: 'value', extra: 'stuff' };

  const tester = genTester(genCall);
  const { actual, expected } = tester(
    yields(
      call(fetch, 'http://httpbin.org/get'),
      respValue, // the result value of `resp` in the generator
    ),
    yields(
      call([respValue, 'json']),
      { data: 'value' }, // the result value of `data` in the generator
    ),
    returnValue,
  );

  t.deepEqual(actual, expected);
});

This is what the test would look like using a manual approach

test('genCall', (t) => {
  const gen = genCall();

  t.plan(4);

  t.deepEqual(
    gen.next().value,
    call(fetch, 'http://httpbin.org/get'),
    'should make http request',
  );

  const respValue = { resp: 'value', json: 'hi' };
  t.deepEqual(
    gen.next(respValue).value,
    call([respValue, 'json']),
    'should get json from response',
  );

  const last = gen.next({ data: 'value' });
  t.ok(last.done, 'generator should finish');
  t.deepEqual(last.value, { data: 'value' }, 'should return data');
});

API

genTester

genTester accepts a generator function and arguments to pass to generator and returns a function that accepts an array of yields, described below:

  • generator (generator function), the generator function to test
  • args (array, default: []), a list of arguments being called with generator
const tester = genTester(generator, arg1, arg2, ...);

tester which is the return value of genTester accepts an array of yields and returns a list of results from the generator at each step

  • yields (array, default: []), a list of yields that the generator will call with the value that will be the result of the yield as well as what was expected of that yield.
const { genTester, yields } = require('gen-tester');

const tester = genTester(someFn);
const results = tester(yields('each', 1), yields('yield', 2), 'and return');
console.log(results);
/*
{
  actual: ['each', 'yield', 'and return'],
  expected: ['each', 'yield', 'and return'],
}
*/

yields

yields is a helper function that will allow the user to send the expected results of a yield as well as the return value of that yield. This is primarily used to inject values into yields for mocking purposes.

  • expected (any), what we expect the yield to yield
  • returns (any), what we want the yield to yield for mocking

skip

skip is a helper function that will allow the user to skip a yield. The generator will progress to the next steps as normal, but we will not keep track of the results or expectations of that yield.

  • returns (any), what we want the yield to yield for mocking
const { skip } = require('gen-tester');

function* test() {
  yield 1;
  const resp = yield call(fetch, 'google.com');
  if (resp.status !== 200) {
    return;
  }
  const val = yield call([resp, 'json']);
  return val;
}

const results = tester(
  skip(),
  yields(call(fetch, 'google.com'), { status: 200 }),
  skip({ with: 'value' }),
  { with: 'value' },
);

throws

throws allows the developer to throw an exception inside a generator.

  • returns (any)
const assert = require('assert');
const { genTester, throws, skip } = require('gen-tester');

function* test() {
  let value = 1;
  try {
    yield 1;
  } catch (err) {
    value = 2;
    yield err + ' handled';
  }

  return value;
}

const tester = genTester(test);
const { actual, expected } = tester(
  yields(1, throws('ERROR')),
  yields('ERROR handled'),
  2,
);
console.log(actual, expected);

assert.deepEqual(actual, expected);

throws can also be used when something throws an exception between yields. When asserting that an exception is raised, you must pass it a function which will receive the error as an argument.

const assert = require('assert');
const { genTester, yields, throws } = require('gen-tester');

function* test() {
  yield 1;
  throw new Error('Something happened');
}

const tester = genTester(test);
const { actual, expected } = tester(
  yields(1);
  throws((error) => error.message === 'Something happened');
);
console.log(actual, expected);

assert.deepEqual(actual, expected);

finishes

finishes ensures that the last step is marked as done by the generator.

const { genTester, finishes } = require('gen-tester');

test('generator finished with finishes', (t) => {
  t.plan(1);

  function* fn() {
    yield 1;
    yield 2;
    return 3;
  }

  const tester = genTester(fn);
  const { actual, expected } = tester(1, 2, finishes(3));
  t.deepEqual(actual, expected);
});

evaluateSteps

evaluateSteps is a helper that takes the results of genTester and determines what steps are no equal and displays more useful information.

const { genTester } = require('gen-tester');
const deepEqual = require('fast-deep-equal');

function* fn() {
  yield 1;
  yield 2;
  return 3;
}

const tester = genTester(fn);
// message gets called when a step is not equal
const message = (actual, expected, index) => {
  return `error on step ${index + 1}:

  actual: ${actual}
  expected: ${expected}`;
};
const { actual, expected } = tester(1, 4, 3);
const results = evauluateSteps({ actual, expected, equal: deepEqual, message });
console.log(results);
/*
{
  message: [Function: message],
  pass: false,
  actual: 2,
  expected: 4,
}
*/

stepsToBeEqual

stepsToBeEqual is a jest matcher that uses evaluateSteps

const { genTester, stepsToBeEqual } = require('gen-tester');

expect.extend({
  stepsToBeEqual,
});

function* fn() {
  yield 1;
  yield 2;
  return 3;
}

const tester = genTester(fn);
const results = tester(1, 2, 3);
expect(results).stepsToBeEqual();