Faker is a tool to generate fake data for testing purposes based on rosie package with some improvements.
To use @mdf.js/faker
you first define a factory. The factory is defined in terms of attributes, sequences, options, callbacks, and can inherit from other factories. Once the factory is defined you use it to build objects.
npm install @mdf.js/faker
yarn add @mdf.js/faker
There are two phases of use:
Factory Definition: Define a factory, specifying attributes, sequences, options, and callbacks:
import { Factory } from '@mdf.js/faker';
interface Player {
id: number;
name: string;
position: string;
}
interface Game {
id: number;
is_over: boolean;
created_at: Date;
random_seed: number;
players: Player[];
}
const playerFactory = new Factory<Player>()
.sequence('id')
.sequence('name', i => {
return 'player' + i;
})
// Define `position` to depend on `id`.
.attr('position', ['id'], id => {
const positions = ['pitcher', '1st base', '2nd base', '3rd base'];
return positions[id % positions.length];
});
const gameFactory = new Factory<Game>()
.sequence('id')
.attr('is_over', false)
.attr('created_at', () => new Date())
.attr('random_seed', () => Math.random())
// Default to two players. If players were given, fill in
// whatever attributes might be missing.
.attr('players', ['players'], players => {
if (!players) {
players = [{}, {}];
}
return players.map(data => playerFactory.build(data));
});
const disabledPlayer = new Factory().extend(playerFactory).attr('state', 'disabled');
Object Building: Build an object, passing in attributes that you want to override:
const game = gameFactory.build({ is_over: true });
// Built object (note scores are random):
//{
// id: 1,
// is_over: true, // overriden when building
// created_at: Fri Apr 15 2011 12:02:25 GMT-0400 (EDT),
// random_seed: 0.8999513240996748,
// players: [
// {id: 1, name:'Player 1'},
// {id: 2, name:'Player 2'}
// ]
//}
You can specify options that are used to programmatically generate the attributes:
import { Factory } from '@mdf.js/faker';
import moment from 'moment';
interface Match {
matchDate: string;
homeScore: number;
awayScore: number;
}
const matchFactory = new Factory<Match>()
.attr('seasonStart', '2016-01-01')
.option('numMatches', 2)
.attr('matches', ['numMatches', 'seasonStart'], (numMatches, seasonStart) => {
const matches = [];
for (const i = 1; i <= numMatches; i++) {
matches.push({
matchDate: moment(seasonStart).add(i, 'week').format('YYYY-MM-DD'),
homeScore: Math.floor(Math.random() * 5),
awayScore: Math.floor(Math.random() * 5),
});
}
return matches;
});
matchFactory.build({ seasonStart: '2016-03-12' }, { numMatches: 3 });
// Built object (note scores are random):
//{
// seasonStart: '2016-03-12',
// matches: [
// { matchDate: '2016-03-19', homeScore: 3, awayScore: 1 },
// { matchDate: '2016-03-26', homeScore: 0, awayScore: 4 },
// { matchDate: '2016-04-02', homeScore: 1, awayScore: 0 }
// ]
//}
In the example numMatches
is defined as an option
, not as an attribute
. Therefore numMatches
is not part of the output, it is only used to generate the matches
array.
In the same example seasonStart
is defined as an attribute
, therefore it appears in the output, and can also be used in the generator function that creates the matches
array.
The convenience function attrs
simplifies the common case of specifying multiple attributes in a batch. Rewriting the game
example from above:
const gameFactory = new Factory()
.sequence('id')
.attrs({
is_over: false,
created_at: () => new Date(),
random_seed: () => Math.random(),
})
.attr('players', ['players'], players => {
/* etc. */
});
You can also define a callback function to be run after building an object:
interface Coach {
id: number;
players: Player[];
}
const coachFactory = new Factory()
.option('buildPlayer', false)
.sequence('id')
.attr('players', ['id', 'buildPlayer'], (id, buildPlayer) => {
if (buildPlayer) {
return [Factory.build('player', { coach_id: id })];
}
})
.after((coach, options) => {
if (options.buildPlayer) {
console.log('built player:', coach.players[0]);
}
});
Factory.build({}, { buildPlayer: true });
Multiple callbacks can be registered, and they will be executed in the order they are registered. The callbacks can manipulate the built object before it is returned to the callee.
If the callback doesn't return anything, @mdf.js/faker
will return build object as final result. If the callback returns a value, @mdf.js/faker
will use that as final result instead.
This is an advanced use case that you can probably happily ignore, but store this away in case you need it.
When you define a factory you can optionally provide a class definition, and anything built by the factory will be passed through the constructor of the provided class.
Specifically, the output of .build
is used as the input to the constructor function, so the returned object is an instance of the specified class:
class SimpleClass {
constructor(args) {
this.moops = 'correct';
this.args = args;
}
isMoopsCorrect() {
return this.moops;
}
}
testFactory = Factory.define('test', SimpleClass).attr('some_var', 4);
testInstance = testFactory.build({ stuff: 2 });
console.log(JSON.stringify(testInstance, {}, 2));
// Output:
// {
// "moops": "correct",
// "args": {
// "stuff": 2,
// "some_var": 4
// }
// }
console.log(testInstance.isMoopsCorrect());
// Output:
// correct
Mind. Blown.
To use @mdf.js/faker
in node, you'll need to import it first:
import { Factory } from '@mdf.js/faker';
// or with `require`
const Factory = require('@mdf.js/faker').Factory;
You might also choose to use unregistered factories, as it fits better with node's module pattern:
// factories/game.js
import { Factory } from '@mdf.js/faker';
export default new Factory().sequence('id').attr('is_over', false);
// etc
To use the unregistered Game
factory defined above:
import Game from './factories/game';
const game = Game.build({ is_over: true });
You can also extend an existing unregistered factory:
// factories/scored-game.js
import { Factory } from '@mdf.js/faker';
import Game from './game';
export default new Factory().extend(Game).attrs({
score: 10,
});
Copyright 2024 Mytra Control S.L. All rights reserved.
Use of this source code is governed by an MIT-style license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.