TypeORM-Entity-Factory
A module for saving bulk entities for E2E database testing.
npm i typeorm-entity-factory --save-dev
State of Library
This module is currently in testing phase. Before version 1.0.0 is released all of the development is considered experimental and is subject to change.
Motivation
For E2E testing it is useful to have sample data within a database for testing queries. Unfortunately, inserting hundreds of entities can be tedious. Furthermore, with relational data, it is time-consuming to ensure that the data is random enough for the testing to be thorough.
This package allows for the bulk creation of TypeORM entities. Each entity has pseudo-random data which can be overridden.
/** Declare the injection container */; /** Retrieve the factories */;;; /** Create single entity with random data and relations. */;/** * Author { * id: 'fc0286d2-5442-4228-bfb5-a98863f002c6', * firstName: 'Lavina', * lastName: 'Maggio', * books: [ * Book { * id: '1af79ea2-5baa-480f-9456-f5b04a7d1c0f', * title: 'Chief Paradigm Assistant', * genre: [Genre] * }, * Book { * id: '3ea7f6b7-477a-4c92-a5c0-3f96c6de9da6', * title: 'Lead Communications Coordinator', * genre: [Genre] * }, * Book { * id: 'ed9637ef-f11b-48b5-bf1a-5ce4960604c2', * title: 'Corporate Integration Architect', * genre: [Genre] * }, * Book { * id: '64f04192-7f5e-4b95-ba6b-76598012f61c', * title: 'Product Implementation Agent', * genre: [Genre] * }, * Book { * id: '2a739e2e-487d-4104-98c0-70454ab36ea2', * title: 'Internal Configuration Consultant', * genre: [Genre] * } * ] * } */ /** Create many entities with random data */;/** * [ * Book { * id: '1eb24072-57eb-46be-8657-6fcaf41aaac1', * title: 'SMS Fresh Brand' * }, * Book { * id: '4b43bb2e-b309-49b9-9f6e-9940aff45ff8', * title: 'Senior Brand Agent' * }, * Book { * id: 'd0f737e3-3ace-4b81-8f14-c3d1773addad', * title: 'Customer Response Director' * }, * Book { * id: '1bbc1e43-e7f4-48cd-9289-c9d68e3c381d', * title: 'District Accounts Engineer' * }, * Book { * id: '9d4acedb-d253-4bb6-8a6b-16e2c6720c60', * title: 'Direct Paradigm Executive' * }, * ] */ /** Pass in optional override values to generate specific data */;/** * Genre { * name: 'Programming', * id: 6637 * } */ /** Combine factories for bulk relational data */;/** * [ * Book { * id: '932f74f8-3dd9-4225-8372-62bce0b47442', * title: 'Chief Configuration Specialist', * genre: Genre { name: 'Programming', id: 6637 } * }, * Book { * id: '63452727-4321-4477-8fd6-1a90dce36693', * title: 'Senior Operations Assistant', * genre: Genre { name: 'Programming', id: 6637 } * }, * Book { * id: '73acc12e-0366-43d9-a7c3-378f824bf9c4', * title: 'Global Program Engineer', * genre: Genre { name: 'Programming', id: 6637 } * }, * Book { * id: '78557247-1e21-4e38-828a-e24405fe8185', * title: 'Forward Tactics Orchestrator', * genre: Genre { name: 'Programming', id: 6637 } * }, * Book { * id: 'eaa40ccd-fa4b-4765-b8d7-e67c3b060025', * title: 'Legacy Web Developer', * genre: Genre { name: 'Programming', id: 6637 } * }, * ] */
Example
This quick-start assumes you already have a TypeORM database: https://github.com/typeorm/typeorm
.
For the examples below we will be using a simple database schema.
In this simple example, an author writes many books, and book belongs to a single genre. These map to the following TypeORM entities:
Creating our first factory
An entity factory generates entities in bulk and stubs them with default data. To start off with we will make the GenreFactory to bulk create Genres. Each factory has an instance of faker for creating random data.
Each entity factory only needs to implement a make() function. The make function returns an instantiated entity class with stub data.
To use our new factory, we need to pass it into init function of the FactoryContainer class.
;
This will inject the database connection into all factories and allow us to use nested factories as described later. The connection must be to the database which has the entity generated by the factories parameter.
Using the container instance we can retrieve an instance of the genre factory.
;
The factory class has two methods of interest makeOne() and makeMany(). These method calls invoke the make method that we defined in our factory.
/** * Create a genre with name 'Romance'. * The saveOne method takes in a override param object. * Any keys within the object which are shared with the entity * will be overridden automatically. */ ; /** * Create five genres with no overrides. * The saveMany method is nearly the same as the saveOne method * but takes in a `count` as a primary argument which decides * how many of that entity it will create. The secondary argument * is the same overrides object as on saveOne. The override object will * be applied to each of the entities created in the saveMany() invocation. */;
Nested Factories
Now that we are familiar with creating factories - lets make a more complicated factory that auto-generates its own relations.
The BookFactory creates its own Genre using the GenreFactory we declared earlier. Factories can be 'nested' in this way for creating data with relations.
Just like the GenreFactory, the BookFactory has to be injected into the container so that all factory references can be resolved at run-time.
;
With our two factories completed, we are now able to bulk create some more sophisticated data.
; /** Create 10 random books - each with their own unique Genre */; /** Pass in optional override values to generate specific data */; /** Combine factories for bulk relational data */;
And finally the AuthorFactory ...
Just like the BookFactory and GenreFactory, we have to add this to container.
;
Invoking saveOne()
on the AuthorFactory class will create five books, each with their own genre.
;await authorFactory.saveOne;/** * Author { * id: 'fc0286d2-5442-4228-bfb5-a98863f002c6', * firstName: 'Lavina', * lastName: 'Maggio', * books: [ * Book { * id: '1af79ea2-5baa-480f-9456-f5b04a7d1c0f', * title: 'Chief Paradigm Assistant', * genre: [Genre] * }, * Book { * id: '3ea7f6b7-477a-4c92-a5c0-3f96c6de9da6', * title: 'Lead Communications Coordinator', * genre: [Genre] * }, * Book { * id: 'ed9637ef-f11b-48b5-bf1a-5ce4960604c2', * title: 'Corporate Integration Architect', * genre: [Genre] * }, * Book { * id: '64f04192-7f5e-4b95-ba6b-76598012f61c', * title: 'Product Implementation Agent', * genre: [Genre] * }, * Book { * id: '2a739e2e-487d-4104-98c0-70454ab36ea2', * title: 'Internal Configuration Consultant', * genre: [Genre] * } * ] * } */
Multiple Factories for an entity
Since the container uses the entity name when retrieving the factory, we need to provide a namespace if we want to use multiple factories for the same entity.
This can be achieved with the namespace key
parameter in the FactoryFor decorator.
For example, if we wanted to have a more specialized version of the AuthorFactory called FamousAuthorFactory.
;;;; /** <-- Additional optional param */
When we want to retrieve the FamousAuthorFactory from the container, we provide the namespace key.
/** This will retrieve the original factory **/; /** This will retrieve the famous author factory **/;
Examples
See the examples directory for integrations. Currently there are example projects for:
- NestJS
Local development
This repository uses docker-compose for it's local development. Please refer to the docker documentation for installing docker onto your machine.
To use this repository for development:
-
Clone the repository:
git clone https://github.com/adamdubicki/typeorm-entity-factory.git
-
Instantiate the development and database container:
docker-compose up -d
The development container is configured with npm and a test suite for experimenting with changes. The docker-compose.yml maps the src files into the container, changes made in your local repository will be reflected in the container.
Note: Disregard installation errors for husky. Husky expects a git path, but the git path is not mounted into the docker container.
-
You can then shell into the development container with
docker exec -it typeorm-entity-factory /bin/bash
. -
From within the container you can run the test suite with
npm run test
.