factory-bot-ts

A simple library for setting up TypeScript objects as test data - heavily inspired by the awesome Ruby's factory_bot

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
factory-bot-ts
1210.1.54 years ago4 years agoMinified + gzip package size for factory-bot-ts in KB

Readme

factory-bot-ts
A simple library for setting up TypeScript objects as test data - heavily inspired by the awesome Ruby's factorybot.
  • Fully written in TypeScript
  • With (optional) type checking
  • With no persistence layer
  • And no promises. ☺️

Installation

1. Add to your project
npm i -D factory-bot-ts
yarn add factory-bot-ts --dev

Basics

1. Ok, suppose we've got a Ninja model..
// ../src/models/ninja.model.ts

export enum NinjaRank {
  GENIN = 'Genin',
  CHUUNIN = 'Chuunin',
  JONIN = 'Jonin'
}

export class Ninja {
  id?: number
  name?: string
  username?: string
  level?: NinjaRank
  sensor?: boolean

  constructor(attrs?: Partial<Ninja>) {
    Object.assign(this, attrs)
  }
}

2. We would define our factories..
// ../src/tests/index.ts

import * as Faker from 'faker'
import { sample } from 'lodash'

import { FactoryBot } from 'factory-bot-ts'

import { Ninja, NinjaRank, Village } from '../src/models'

FactoryBot.define('ninja', {
  id: () => Faker.random.number(),
  name: () => Faker.name.findName(),
  username: () => FactoryBot.seq(seq => `${Faker.internet.userName()}_${seq}`),
  level: () => FactoryBot.rand(NinjaRank),
  sensor: () => sample([true, false])
}, Ninja)

FactoryBot.define('village', {
  id: () => Faker.random.uuid(),
  name: () => Faker.name.findName(),
  members: () => FactoryBot.buildList<Ninja>('ninja', 2)
}, Village)

export {
  FactoryBot
}

3. And then we'd just use them on our tests!
// ../src/models/ninja.model.spec.ts

import { FactoryBot } from '../src/tests'

import { Ninja, NinjaRank, Village } from '.'

export class NinjaSpec {
  let instance: Ninja

  before(() => {
    instance = FactoryBot.build('ninja', { name: 'Kakashi Hatake' })
  })

  it('builds a Ninja! 👹', () => {
    expect(instance).to.be.an
      .instanceof(Ninja)
  })
}

More

Factory-bot-ts also allow us to..
1. Define untyped factories with static data
FactoryBot.define('ninja', {
  id: 1,
  name: 'Sasuke Uchiha',
  username: 'sasuke',
  level: NinjaRank.GENIN,
  sensor: false,
  sharingan: true
})

FactoryBot.build('ninja') /* => {
  id: 1,
  name: 'Sasuke Uchiha',
  username: 'sasuke',
  level: 'Genin',
  sensor: false,
  sharingan: true
} */

2. Define factories with type checking
FactoryBot.define<Ninja>('ninja', {
  id: 1,
  name: 'Sasuke Uchiha',
  username: 'sasuke',
  level: NinjaRank.GENIN,
  sensor: false
}, Ninja)

FactoryBot.build('ninja') /* => Ninja {
  id: 1,
  name: 'Sasuke Uchiha',
  username: 'sasuke',
  level: 'Genin',
  sensor: false
} */

3. Define factories with Dynamic data
FactoryBot.define('ninja', {
  id: () => Faker.random.number(),
  name: () => Faker.name.findName(),
  username: () => Faker.internet.userName(),
  level: () => FactoryBot.rand(NinjaRank),
  sensor: () => sample([true, false])
}, Ninja)

FactoryBot.build('ninja') /* => Ninja {
  id: 43748,
  name: 'Martine Romaguera MD',
  username: 'Kaleb_Homenick',
  level: 'Jonin',
  sensor: true
} */

4. Define factories with custom, random and sequenced data
FactoryBot.define('ninja', {
  id: () => Faker.random.number(),
  name: () => Faker.name.findName(),
  username: () => FactoryBot.seq(seq => `${Faker.internet.userName()}_${seq}`),
  level: () => FactoryBot.rand(NinjaRank),
  sensor: () => sample([true, false])
}, Ninja)

FactoryBot.build<Ninja>('ninja', { name: 'Uzimaki Naruto' }) /* => Ninja {
  id: 11941,
  name: 'Uzimaki Naruto',
  username: 'Art_Crist36_1',
  level: 'Genin',
  sensor: true
} */

FactoryBot.build<Ninja>('ninja', { name: 'Sasuke Uchiha', username: 'sasuke-kun' }) /* => Ninja {
  id: 52565,
  name: 'Sasuke Uchiha',
  username: 'sasuke-kun',
  level: 'Chuunin',
  sensor: false
} */

5. Define chained factories
FactoryBot.define('ninja', {
  id: () => Faker.random.number(),
  name: () => Faker.name.findName(),
  username: () => FactoryBot.seq(seq => `${Faker.internet.userName()}_${seq}`),
  level: () => FactoryBot.rand(NinjaRank),
  sensor: () => sample([true, false])
}, Ninja)

FactoryBot.define('village', {
  id: () => Faker.random.uuid(),
  name: 'Leaf',
  members: () => FactoryBot.buildList<Ninja>('ninja', 2)
}, Village)

FactoryBot.build('village') /* => Village {
  id: '7ec17407-cfdb-4a3f-b434-de788eb41591',
  name: 'Leaf',
  members: [ Ninja {
    id: 11941,
    name: 'Herta Hane',
    username: 'Kaleb_Homenick_5',
    level: 'Genin',
    sensor: true
  }, Ninja {
    id: 52565,
    name: 'Corbin Koss',
    username: 'Art_Crist36_6',
    level: 'Genin',
    sensor: false
  }]
} */

6. Extend existing factories in order to generate specialized data
FactoryBot.define<Ninja>('ninja', {
  id: 1,
  name: 'Kakashi Hatake',
  username: 'kakashi',
  level: NinjaRank.GENIN,
  sensor: false
}, Ninja)

FactoryBot.extend<Ninja>('ninja', 'jōnin', {
  level: NinjaRank.JONIN
})

FactoryBot.build<Ninja>('ninja') /* => Ninja {
  id: 1,
  name: 'Kakashi Hatake',
  username: 'kakashi',
  level: Genin,
  sensor: false
} */

FactoryBot.build<Ninja>('jōnin') /* => Ninja {
  id: 1,
  name: 'Kakashi Hatake',
  username: 'kakashi',
  level: Jōnin,
  sensor: false
} */

For more examples, please, check out the project's specs.

Dependencies

To run this project we need to have:

Development

  1. Install the dependencies above
  2. $ git clone https://github.com/roalcantara/factory-bot-ts.git - Clone the project
  3. $ cd factory-bot-ts - Go into the project folder
  4. $ yarn - Run the setup script

Running specs

$ yarn test to run the specs

How to contribute

Code of Conduct

Everyone interacting in the factory-bot-ts project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

License

The package is available as open source under the terms of the CC BY-SA 4.0.