Factory - fixtures replacement tool for Node
Factory is a fixtures replacement tool. It aims to replace static and hard to maintain fixtures with factories đźŹ.
In javascript/typescript projects where the test codebase grew larger and larger, I created lots of objects with random or hardcoded values in my integration test setups. Such task becomes tedious when you get numerous mandatory attributes, and many objects dependencies/relationships that you need to create. And all of that before even writing your first line of “real test”. Fixtures can become a pain.
For example, we could have something like:
// Hypothesis: we're using something like typeorm with Entity repository
it("myTestedFunction should do xxx with profile and user", async () => {
// Given
const user = new User({
name: 'John',
phone: '+555123456',
mail: 'john@mail.com',
age: 35,
description: 'Foo bar'
});
await user.save();
const profile = new Profile({
extraData: 'Foo',
user
});
await profile.save();
// ...
// ...
// When
const result = await myTestedFunction(profile);
// Then
expect(result).toBeTruthy();
})
While this worked fine for a while, complex tests became unmanageable. It’s long to write, long to read and understand, and hard to maintain.
I tried many different libraries but none suited my needs. I used for a very long time factory-boy (also inspired by factory_bot project) in python projects and I wanted something close to that approach.
I found factory-builder to be a good start but still, it wasn’t exactly what I wanted. It is quite generic and nice to use but it sadly lacks direct typeorm integration. I wanted a direct integration with ORMs, or at least a link to predefined models to leverage type hints and an easier integration with ORMs.
If I wanted specifically a closer integration with typeorm to start with, I also plan to move away from it and use the same library for others ORMs or use cases.
Meet @adrien-may/factory
That’s why I created factory. From the example above, we could now have something like:
// Factories init
const userFactory = new UserFactory();
const profileFactory = new ProfileFactory();
// Usage
it("myTestedFunction should do xxx with profile and user", async () => {
// Given
const user = await userFactory.create();
const profile = await profileFactory.create({ user });
// ...
// When
const result = await myTestedFunction(profile);
// Then
expect(result).toBeTruthy();
})
We could even define our profile factory so that the user step becomes useless.
Features
With the first released version, factory offers to create instances from defined factories which take model
and attrs
as properties.
Factories looks pretty much like the following:
class UserFactory extends Factory<User> {
model = User
attrs = {
firstName: () => chance.first(),
lastName: () => chance.last(),
email: () => chance.email(),
phone: () => chance.phone(),
profile: new SubFactory(ProfileFactory)
}
}
So far, we can use it with or without typeorm. Further development would be needed to add compatibility with others ORMs/approach (through new adapters).
We could think of other use cases but for now, here are the implemented features:
- Values resolution from hardcoded values or functions
- Fuzzy generation with Faker, Chance or others (implied by value resolution from functions)
- subFactories to re-use factories for relationships (one to one, many to one etc…)
-
Sequences ex: mail ~= email${n}@example.com
with an auto-incremented “n” value -
lazyAttributes ex: mail ~= ${firstName}@example.com
where firstName is resolved from the entity -
lazySequence to leverage both sequence and lazyAttributes ex: mail ~= ${firstName}-${n}@example.com
- PostGeneration hook to trigger action once instance is created
Alternative projects:
Hope you will find this library useful. Any contribution is welcome. Happy coding, cheers 🍻