manufactory

Setup JavaScript objects as test data

Usage no npm install needed!

<script type="module">
  import manufactory from 'https://cdn.skypack.dev/manufactory';
</script>

README

Manufactory

Manufactory is lightweight JavaScript library for building JavaScript objects for test data.

Installation

Install via npm as a development dependency:

$ npm install manufactory --save-dev

Getting Started

Defining factories

While Manufactory doesn't enforce a strict directory structure nor expects specific naming, we recommend to define your factories in a test/factories directory. Each factory should match the name of the object that you are building. For example, if the given factory represents the Post model, we recommend to define it in test/factories/Post.factory.js.

// test/factories/Post.factory.js

import Factory from 'manufactory';

export default Factory()
  .setName('Post')
  .attr('title', 'Awesome Post')
  .attr('slug', (i) => `awesome-post-${i}`)
  .attr('name.first', 'John')
  .attr('name.last', 'Doe');

Using factories

import PostFactory from 'test/factories/Post.factory.js';

// defaults
const post1 = PostFactory();

post1.title // => 'Awesome Post'
post1.name.first // => 'John'
post1.name.last // => 'Doe'
post1.slug // => 'awesome-post-1'

// overriding defaults
const post2 = PostFactory({ title: 'Other title' });

post2.title // => 'Other title'
post2.name.first // => 'John'
post2.name.last // => 'Doe'
post2.slug // => 'awesome-post-2'

Defining Attributes

Static values:

const PostFactory = Factory()
  .attr('title', 'Awesome Post');

PostFactory().title // => 'Awesome Post'

Dynamic values (aka lazy values):

const PostFactory = Factory()
  .attr('title', function() {
    return 'Awesome Post';
  });

PostFactory().title // => 'Awesome Post'

Sequences:

const PostFactory = Factory()
  .attr('title', function(i) {
    return `Awesome Post ${i}`;
  });

PostFactory().title // => 'Awesome Post 1'
PostFactory().title // => 'Awesome Post 2'

Nested objects:

const PostFactory = Factory()
  .setName('Post')
  .attr('name.first', 'John')
  .attr('name.last', 'Doe');

PostFactory().name.first // => 'John'
PostFactory().name.last // => 'Doe'

For more details - visit the pathval project.

Type conversion:

const PostFactory = Factory()
  .attr('number', '1', Number)

PostFactory().number // => 1

Mixins

You can mixin other factories in order to share builders, attributes and middlewares. Imagine that you have different types of post. You can define a Base factory and mix it into its variations:

const SlugFactory = Factory()
  .attr('slug', (i) => `slug-${i}`);

const BasePostFactory = Factory()
  .attr('title', 'Title');

const PublishedPost = Factory()
  .mixin(BasePostFactory)
  .mixin(SlugFactory)
  .attr('state', 'published');

const TrashedPost = Factory()
  .mixin(BasePostFactory)
  .mixin(SlugFactory)
  .attr('state', 'trashed');

const publishedPost = PublishedPost();
publishedPost.title // => 'Title'
publishedPost.slug // => 'slug-1'
publishedPost.state // => 'published'

const trashedPost = TrashedPost();
trashedPost.title // => 'Title'
trashedPost.slug // => 'slug-2'
trashedPost.state // => 'trashed'

Builder

The builder is a function that will be invoked with all compiled attributes, the name of the factory and the root name. Each factory can have its own builder. The default builder is Object.

With Builders you can easily modify your factories to be persisted into a database or cast them to whatever your system expects.

import Immutable from 'immutable';

const PostFactory = Factory()
  .setBuilder(Immutable.Map)
  .attr('title', 'Awesome Post');

PostFactory().get('title') // => 'Awesome Post'
PostFactory() instanceof Immutable.Map // => true

const DBBuilder = (attrs, name, rootName) => {
  // pseudo code
  return getModel(name).create(attrs); // returns a Promise
};

const PostFactory = Factory()
  .setBuilder(DBBuilder)
  .attr('title', 'Awesome Post');

PostFactory().then((factory) => {
  // the `factory` is now persisted in the db
});

Middlewares

Middlewares are functions that receive the compiled attributes and can modify them as they wish. It's a great way to share functionality across different projects.

const middleware = (attrs) => {
  attrs.title = `Super ${attrs.title}`;
  return attrs;
};

const PostFactory = Factory()
  .use(middleware)
  .attr('title', 'Awesome Post');

PostFactory().title // => 'Super Awesome Post'

Factory name

Each factory can have its own name. Setting a name is optional and can be useful in situations where you have to specify the name of the object that you are building, so it can be used in the builder later:

const PostFactory = Factory()
  .setName('Post')

Development

$ npm install

Tests

$ npm test

Test coverage

$ npm run coverage

Linters

$ npm run lint

License

MIT (Product Hunt Inc.)