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

feathers-factory

v4.0.2

Published

Create data factories for your Feathers services

Downloads

151

Readme

Feathers Factory

A dead easy way to mock data for testing your Feathers services. Heavily inspired by Meteor Factory

Tests Downloads Version

Installation

npm install --save-dev feathers-factory

Example Usage

Feathers Factory provides an easy way define data generation templates for testing Feathers services. This works really nicely together with a mock data generator like faker.

Define and export a factory for your tests

Just new up the Feathers Factory class, provide your Feathers service, and a "Generator" object which defines what values should be inserted into your service when the factory is called.

// ./tests/Factories.ts
import FeathersApp from '../src/App'
import { Factory } from 'feathers-factory';

export const UserFactory = new Factory(FeathersApp.service('users'), {
    username: () => Faker.internet.userName(),
    membership: 'bronze'
});

Types for the factory are inferred from the Feathers service you provide. It's fairly strict by design, so it may give you some issues depending on how your service is defined. See Global Factories for more info on how to work around this.

Import and use your factory

// ./tests/services/users.test.ts
import { UserFactory } from '../../Factories';
import FeathersApp from '../../../src/App';

describe('Users', () => {
    const service = FeathersApp.service('users');
    
    it('can be removed', async () => {
        const user = await UserFactory.create(); 
        // { _id: "507f191e810c19729de860ea", username: "Damaris8", membership: "bronze" }
    
        await expect(service.get(user._id)).resolves.toHaveProperty('_id', user._id);
        
        await service.remove(user._id)
        
        await expect(service.get(user._id)).rejects.toBeInstanceOf(NotFound);
    })
})

Advanced usage

Use promises and other factories

You're not limited to functions and static data! Your factories can call on other factories to ensure there any relational data is also created for your service.


export const CommentFactory = new Factory(FeathersApp.service('/posts/comments'), {
    // You can use promises
    async content() {
        const response = await Axios.get('https://jsonplaceholder.typicode.com/posts/1/comments');
        return response[0].body;
    },

    // Service depends on a relationship? No problem! Define a `user` factory and call it:
    async userId() {
        return (await Factory.create('user'))._id;
    }
})  

Override properties

You don't need to create a whole new factory if you just need to override one bit of data for a test. For example, if you want to test against comments left by a user with "Gold" membership for example.

const goldUser = await UserFactory.create({ membership: 'gold' });
// { _id: "00000020f51bb4362eee2a4d", username: "Eliseo2", membership: "gold" }

const commentByGoldUser = await CommentFactory.create({
    userId: goldUser._id,
})
// { _id: "507f191e810c19729de860ea", "userId": "00000020f51bb4362eee2a4d", content: "lorem ipsum..." }

Use a property from the current factory

Using this, you can reference properties from the factory result. All properties are promises, so you may need to await the property if you're going to modify it.

This can be particularly if your service depends on relational data belonging to the same entity.

export const OrderFactory = new Factory(FeathersApp.service('/merchant/orders'), {
    customerEmail: () => Faker.internet.email(),
    async merchantId() {
        const merchant = await MerchantFactory.create();
        return merchant._id;
    },
    async productId() {
        const merchantId = await this.merchantId;
        const product = await ProductFactory.create();
        return product._id;
    }
})

See clues.js for more details on how this works.

Setting default service create() params

You can assign default create() params. Handy if your Feathers service hooks rely on some Hook context params for handling the create() requests fired by Feathers-Factory.

export const CommentFacoryWithSlugs = new Factory(FeathersApp.service('/posts/comments'), {
    message: Faker.lorem.sentence,
    async userId() {
        return (await UserFactory.create())._id
    },
}, {
    async query() {
        return {
            postSlug: (await PostFactory.create()).slug
        }
    }
})

Overriding params for a one-off create() call

You can override default service create params. It's worth noting that merging with defaults only goes one level deep.

const SpecialComment = await CommentFactoryWithSlugs.create({}, {
    query: {
        postSlug: 'foo-bar'
    }
});

Creating multiple entries

You can create multiple database entries using the createMany() method. Handy if you need to generate a lot of data for a particular service.

await CommentFactoryWithSlugs.createMany(1337, {}, {
    query: {
        postSlug: 'foo-bar'
    }
});

Only fetch data

You can resolve the factory data without inserting it into the database using the Factory get() method.

await UserFactory.get({ username: 'phantom-user99' });
// { username: "phantom-user99", membership: "bronze" }

Prepare factory result, but don't insert it into the service

You can even use this to fetch the result of your factory methods. Notice merchantId is accessed as if it was a property. See clues.js for more info on how this works.

Factory.create('order', {
    email: Faker.internet.email,
    async merchantId() {
        return (await Factory.create('merchant'))._id;
    },
    async productId() {
        return (await Factory.create('product', { merchantId: await this.merchantId }))
    } 
})

Alternative usage

Define a global factory

Type inference here is not as good as with explicitly exported Factory modules. But can be a good fallback if the schema provided by your Feathers service is giving you type issues. It's also pretty handy if you're working in a non-TypeScript environment.

import { GlobalFactories } from 'feathers-factory';

GlobalFactories.define('my-factory', FeathersApp.service('service-to-mock-for'), {
    
    // Define dynamic data. Perfect with Faker.
    email() {
        return Faker.internet.email();
    },
    
    // Or just define static data.
    servicePlan: 'free',
    
});

Run the factory anywhere you need to mock a database entry with random data:

export default async (FeathersApp) => {
    const user = await Factory.create('my-factory');
    
    console.log(user); 
    // -> { _id: "507f191e810c19729de860ea", email: "[email protected]", servicePlan: "free" }
    
    await FeathersApp.get(user._id)
    // -> { _id: "507f191e810c19729de860ea", email: "[email protected]", servicePlan: "free" }
};

How does it work?

Pretty simple - any property, function, method or promise you define in the factory specification is resolved whenever you call Factory.create(), keeping your object structure, but using the return type of all properties within your factory.

For example, a factory generator like the following

{ 
    foo: () => 'bar'
    hello: async () => 'world'
    someNumber: 22
}

Resolves to:

{ foo: 'bar', hello: 'world', someNumber: 22 }

The resolved data is then passed directly into your Feathers service through its create() method.

Credit

Thanks to clues.js for providing an excellent library for resolving in-object data.

License

This repository is licensed under the ISC license.

Copyright (c) 2019, Jørgen Vatle.