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

@fixture-factory/fixture-factory

v1.0.1

Published

Modest fixture creation utility for JS tests

Downloads

34

Readme

FixtureFactory

Modest fixture creation utility for JS tests

Installation

You can install the utility using npm or yarn:

$ npm install --save-dev @fixture-factory/fixture-factory
$ yarn add -D @fixture-factory/fixture-factory

Usage

You would typically use this utility to create one or more objects with a specific set of keys, differentiating values as necessary. Let's assume a User class with the following properties:

// src/user.js
class User {
  constructor(id, name, username) {
    this.id = id;
    this.name = name;
    this.username = username;
    this.status = true;
    this.authLevel = 0;
  }

  stringify() {
    const levelNames = ['user', 'moderator', 'administrator'];
    return `${this.id}: ${this.username} (${this.name}) ${levelNames[this.authLevel]}`;
  }
}

export default User;

You can create a fixture factory for the User class and define its traits based on your application's business logic:

// fixtures/user.js
import FixtureFactory from '@fixture-factory/fixture-factory';
import User from '../src/user';

const factory = new FixtureFactory();

factory.sequence('id', function* () {
  let id = 0;
  while (true) yield id++;
});

factory.define('User', () => new User(0, '', ''))
  .trait('with id', id => ({ id }))
  .trait('with auto id', () => ({ id: factory.nextFrom('id') }))
  .trait('moderator', { authLevel: 1 })
  .trait('administrator', { authLevel: 2 })
  .trait('active', { status: true })
  .trait('inactive', { status: false })
  .trait('with name', name => ({ name }))
  .trait('with username', username => ({ username }))
  .trait('deleted', ['inactive', ['with name', 'deleted']]);

factory.alias(
  'Superuser',
  'User',
  'administrator',
  'active',
  ['with name', 'superuser'],
  ['with username', 'superuser']
);

factory.alias('Bot', 'User', 'moderator');

export default factory.package();

Finally, you can use it in your tests to generate appropriate fixtures:

// test/user.js
import User from '../src/user';
import userFactory from '../fixtures/user';

let fixtures = { users: [], bots: [] };

const botNames = ['tic', 'tac', 'toe'];

fixtures.users = userFactory.createMany('User', 5, [
  'with auto id',
  'active',
]);

fixtures.superuser = userFactory.create('Superuser');

fixtures.bots = userFactory.createMany('Bot', 8, i => [
  ['with id', 1000 + i],
  ['with name', `${botNames[Math.floor(Math.random() * 3)]}_bot`],
]);

fixtures.users.push(userFactory.create('User', 'deleted'));

describe('User', () => {
  describe('stringify()', () => {
    it('for a regular user', () => {
      expect(fixtures.users[2].stringify().startsWith('2: ')).toBeTruthy();
    });

    it('for a superuser', () => {
      expect(fixtures.superuser.stringify()).toBe(
        '0: superuser (superuser) administrator'
      );
    });

    it('for a bot', () => {
      expect(fixtures.bots[1].name.includes('_bot')).toBeTruthy();
    });
  });
});

API reference

Creating factory instances

Factory instances can be created using the FixtureFactory constructor.

const factory = new FixtureFactory();

Return value

A FixtureFactory instance.

Defining factories

Factories can be defined using FixtureFactory.prototype.define(name, initializer).

import User from './user';

const factory = new FixtureFactory();

// With a constructor
factory.define('User', User);

// With a function
factory.define('Item', () => ({ itemId: 0 }));

Parameters

  • name: A string representing the name of the factory. Each name must be unique.
  • initializer: A function or a class. If a class is provided, its constructor will be called for fixture creation. The initializer function or constructor will not be passed any arguments and is used to set the initial value for a fixture.

Return value

A new FactoryDefinition which can be used to define traits.

Adding traits

Traits can be chained to any existing definition using FactoryDefinition.prototype.trait(name, definition).

const factory = new FixtureFactory();

factory
  .define('Item', () => ({ itemId: 0 }))
  // With an object
  .trait('active', { active: true })
  // With a function
  .trait('named', name => ({ name }))
  // With an array
  .trait('special item', ['active', ['named', 'Special']]);

Parameters

  • name: A string representing the name of the trait. Each name must be unique.
  • definition: An object, a function or an array.
    • If an object is provided, it will be merged into the initial value of the fixture.
    • If a function is provided, it must return an object. The returned object will be merged into the initial value of the fixture.
    • If an array is provided, all values must be either a string or a tuple of a string and parameters. Each value represents an existing trait to be inherited.

Return value

The FactoryDefinition for which the method was called.

Creating aliases

An alias is a thin layer on top of an existing definition, inheriting any traits defined up to that point. Aliases can be defined using FixtureFactory.prototype.alias(aliasName, name, ...params).

const factory = new FixtureFactory();

factory
  .define('Product', { id: 0 })
  .trait('category', category => ({ category }))
  .trait('manufacturer', manufacturer => ({ manufacturer }))
  .trait('in stock', { inStock: true });

factory
  .alias('Laptop', 'Product', ['category', 'laptops'], 'in stock'];

Parameters

  • aliasName: A string representing the name of the alias. Each name must be unique.
  • name: The name of the original definition.
  • ...params: Any traits (string or tuple) to pass by default to the original definition.

Return value

A new FactoryDefinition for the defined alias.

Creating individual fixtures

Individual fixtures can be created using FixtureFactory.prototype.create(name, ...params).

const factory = new FixtureFactory();

factory
  .define('Item', () => ({ itemId: 0 }))
  .trait('active', { active: true })
  .trait('named', name => ({ name }));

const myItem = factory
  .create('Item', 'active', ['name', 'Laptop']);
// { itemId: 0, active: true, name: 'Laptop' }

Parameters

  • name: A string for the definition to be used to create the fixture object.
  • ...params: Traits (string or tuple) to be given to the fixture object.

Return value

A fixture object.

Creating multiple fixtures

Multiple fixtures can be created using FixtureFactory.prototype.createMany(name, num, paramMap).

const factory = new FixtureFactory();

factory
  .define('Item', () => ({ itemId: 0 }))
  .trait('active', { active: true })
  .trait('category', category => ({ category }))
  .trait('id', itemId => ({ itemId }));

// With array
const laptops = factory
  .createMany('Item', 2, ['active', ['category', 'Laptop']]);
// [
//  { itemId: 0, active: true, category: 'Laptop' },
//  { itemId: 0, active: true, category: 'Laptop' }
// ]

// With function
const numberedItems = factory
  .createMany('Item', 3, (id) => ['id', id]);
// [ { itemId: 0 }, { itemId: 1 }, { itemId: 2} ]

Parameters

  • name: A string for the definition to be used to create the fixture object.
  • num: A number representing the amount of fixture objects to be created.
  • paramMap: An array of traits (string or tuple) to be passed down to all objects or a function that returns an array of traits. If a function is provided, it accepts the index of the item being created as the sole argument.

Return value

An array of fixture objects.

Defining sequences

Sequences are simple generators that can be used when creating fixtures. They can be defined using FixtureFactory.prototype.sequence(name, generator).

const factory = new FixtureFactory();

factory
  .sequence('id', function* () {
    let id = 0;
    while (true) yield id++;
  });

Parameters

  • name: A string representing the name of the sequence. Each name must be unique.
  • generator: A generator function.

Return value

A generator.

Using sequences

Sequences can be called to generate sequential values using FixtureFactory.prototype.nextFrom(name).

const factory = new FixtureFactory();

factory
  .sequence('id', function* () {
    let id = 10;
    while (true) yield id++;
  });

factory
  .define('Item', () => ({ itemId: 0 }))
  .trait('autoId', () => ({ itemId: factory.nextFrom('id') }));

const numberedItems = factory
  .createMany('Item', 3, ['autoId']);
// [ { itemId: 10 }, { itemId: 11 }, { itemId: 12 }]

Parameters

  • name: A string for the sequence definition to be used to generate a new value.

Return value

The next value in the given sequence.

Packaging factories

Factories can be packaged along with any definitions, aliases, sequences and traits they contain using FixtureFactory.prototype.package(). Packaged factories only expose their create(), createMany() and nextFrom() methods and any contained definitions cannot be altered further.

This is especially useful when exporting definitions from a file to use in multiple test files.

const factory = new FixtureFactory();

factory
  .define('Item', () => ({ itemId: 0 }))
  .trait('active', { active: true })
  .trait('category', category => ({ category }))
  .trait('id', itemId => ({ itemId }));

export default factory.package();

Return value

A packaged object from the current factory's contents.

License

This project is licensed under the MIT license.