owl1n/typeorm-factories
Create factories for your TypeORM entities. Useful for NestJS applications
English | Русский
A library for creating TypeORM entity factories to simplify test data generation in NestJS applications.
When writing unit tests, you often need to create entity instances with populated data. Instead of manually creating objects in every test, factories allow you to:
pnpm add -D typeorm-factories @faker-js/faker
# or
npm install --save-dev typeorm-factories @faker-js/faker
The library uses @faker-js/faker to generate fake data.
Create a factory file (e.g., factories/task.factory.ts):
import { faker } from '@faker-js/faker';
import { define } from 'typeorm-factories';
import { Task } from '../src/entities/task.entity';
define(Task, (fakerInstance) => {
const task = new Task();
task.id = fakerInstance.string.uuid();
task.title = fakerInstance.lorem.sentence();
task.description = fakerInstance.lorem.paragraph();
task.completed = fakerInstance.datatype.boolean();
task.createdAt = fakerInstance.date.past();
return task;
});
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FactoryModule, factory } from 'typeorm-factories';
import { TasksService } from './tasks.service';
import { Task } from './entities/task.entity';
describe('TasksService', () => {
let service: TasksService;
let repository: Repository<Task>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [FactoryModule],
providers: [
TasksService,
{
provide: getRepositoryToken(Task),
useValue: {
findOne: jest.fn(),
save: jest.fn(),
find: jest.fn(),
},
},
],
}).compile();
await module.init();
service = module.get<TasksService>(TasksService);
repository = module.get<Repository<Task>>(getRepositoryToken(Task));
});
describe('create', () => {
it('should create a new task', async () => {
const taskData = await factory(Task).make();
jest.spyOn(repository, 'save').mockResolvedValue(taskData);
const result = await service.create(taskData);
expect(result).toEqual(taskData);
expect(repository.save).toHaveBeenCalledWith(taskData);
});
});
describe('findCompleted', () => {
it('should return only completed tasks', async () => {
const completedTasks = await factory(Task)
.makeMany(3, { completed: true });
jest.spyOn(repository, 'find').mockResolvedValue(completedTasks);
const result = await service.findCompleted();
expect(result).toHaveLength(3);
expect(result.every(task => task.completed)).toBe(true);
});
});
});
No download data available
No tracked packages depend on this.
define(Entity, factoryFunction)Registers a factory for an entity.
Parameters:
Entity: TypeORM entity classfactoryFunction: Function that receives a Faker instance and optional settings, returns a populated entitydefine(User, (faker) => {
const user = new User();
user.email = faker.internet.email();
user.name = faker.person.fullName();
return user;
});
factory(Entity, settings?)Creates an EntityFactory instance for generating entity objects.
Parameters:
Entity: Entity classsettings (optional): Additional settings for the factoryReturns: EntityFactory<Entity, Settings>
make(overrideParams?)Creates a single entity instance.
const task = await factory(Task).make();
// With field overrides
const urgentTask = await factory(Task).make({
priority: 'high',
dueDate: new Date('2024-12-31')
});
makeMany(count, overrideParams?)Creates an array of entity instances.
// Create 5 tasks
const tasks = await factory(Task).makeMany(5);
// Create 3 tasks with "completed" status
const completedTasks = await factory(Task).makeMany(3, { completed: true });
map(callback)Applies a function to each created object. Useful for additional processing.
const tasksWithTimestamps = await factory(Task)
.map(async (task) => {
task.updatedAt = new Date();
return task;
})
.makeMany(5);
Factory functions can be asynchronous for complex data generation:
define(User, async (faker) => {
const user = new User();
// Simulate async operations (API calls, file I/O, etc.)
user.email = await someAsyncEmailGenerator();
user.avatar = await fetchRandomAvatar();
user.name = faker.person.fullName();
return user;
});
// Works seamlessly with all methods
const user = await factory(User).make();
const users = await factory(User).makeMany(5);
Generate unique sequential values for each entity:
define(User, (faker, settings, sequence) => {
const user = new User();
user.email = `user${sequence}@example.com`;
user.username = `user_${sequence}`;
user.name = faker.person.fullName();
return user;
});
// Creates users with emails: user0@, user1@, user2@
const users = await factory(User).makeMany(3);
Define reusable modifications to your entities:
define(User, (faker) => {
const user = new User();
user.email = faker.internet.email();
user.role = 'user';
user.status = 'active';
return user;
})
.state('admin', (user) => {
user.role = 'admin';
user.permissions = ['read', 'write', 'delete'];
return user;
})
.state('suspended', (user) => {
user.status = 'suspended';
user.suspendedAt = new Date();
return user;
})
.state('premium', async (user) => {
user.subscriptionTier = 'premium';
user.subscriptionEndsAt = faker.date.future();
return user;
});
// Apply single state
const admin = await factory(User).state('admin').make();
// Apply multiple states
const suspendedAdmin = await factory(User)
.states(['admin', 'suspended'])
.make();
Execute code before or after entity creation:
define(User, (faker) => {
const user = new User();
user.email = faker.internet.email();
user.password = 'plain-password';
return user;
})
.beforeMake(async (user) => {
// Hash password before creating
user.password = await bcrypt.hash(user.password, 10);
})
.afterMake(async (user) => {
// Log or perform additional setup
console.log('User created:', user.email);
});
const user = await factory(User).make();
// Password is automatically hashed
Automatically create related entities:
define(Post, (faker) => {
const post = new Post();
post.title = faker.lorem.sentence();
post.content = faker.lorem.paragraphs();
return post;
})
.association('author', User)
.association('comments', Comment, { count: 3 });
// Automatically creates a User and 3 Comments
const post = await factory(Post).make();
console.log(post.author); // User instance
console.log(post.comments); // Array of 3 Comment instances
Create entities without using Faker (useful for mocks):
const userMock = factory(User).build({
id: '123',
email: 'test@example.com',
name: 'Test User',
});
// Returns a plain object without calling faker
expect(userMock.email).toBe('test@example.com');
All features can be combined for complex scenarios:
define(User, (faker, settings, sequence) => {
const user = new User();
user.email = `user${sequence}@example.com`;
user.name = faker.person.fullName();
user.role = 'user';
return user;
})
.state('withPosts', async (user) => {
user.posts = await factory(Post).makeMany(5, { authorId: user.id });
return user;
})
.beforeMake(async (user) => {
user.createdAt = new Date();
})
.association('profile', UserProfile);
// Create an admin user with posts and profile
const admin = await factory(User)
.state('withPosts')
.make({ role: 'admin' });
interface UserSettings {
role: 'admin' | 'user';
}
define(User, (faker, settings?: UserSettings) => {
const user = new User();
user.email = faker.internet.email();
user.name = faker.person.fullName();
user.role = settings?.role || 'user';
return user;
});
// Usage
const admin = await factory(User, { role: 'admin' }).make();
const regularUser = await factory(User, { role: 'user' }).make();
Factories can automatically resolve nested entities:
define(Comment, (faker) => {
const comment = new Comment();
comment.text = faker.lorem.paragraph();
comment.author = factory(User).make(); // Returns a Promise
return comment;
});
// Nested entity will be automatically resolved
const comment = await factory(Comment).make();
console.log(comment.author); // User object
define(Post, (faker) => {
const post = new Post();
post.title = faker.lorem.sentence();
post.content = faker.lorem.paragraphs();
return post;
});
define(Comment, (faker) => {
const comment = new Comment();
comment.text = faker.lorem.paragraph();
return comment;
});
// Creating a post with comments
const post = await factory(Post).make();
const comments = await factory(Comment).makeMany(3, { postId: post.id });
Reset sequence counters between tests to ensure consistent data:
import { resetSequences } from 'typeorm-factories';
describe('UserService', () => {
beforeEach(() => {
resetSequences(); // Reset all sequence counters
});
it('creates users with sequential emails', async () => {
const users = await factory(User).makeMany(2);
expect(users[0].email).toBe('user0@example.com');
expect(users[1].email).toBe('user1@example.com');
});
});
It's recommended to keep factories in a separate directory:
your-project/
├── src/
│ ├── entities/
│ │ ├── user.entity.ts
│ │ └── task.entity.ts
│ └── ...
├── factories/
│ ├── user.factory.ts
│ └── task.factory.ts
└── test/
└── ...
The library automatically finds all files matching the pattern **/*.factory.{js,ts} when the module initializes.
FactoryModule is imported into a test module, it scans the project for factory filesfactory() function retrieves the registered factory by entity classmake() and makeMany() methods use Faker to generate datafactories/task.factory.ts:
import { faker } from '@faker-js/faker';
import { define } from 'typeorm-factories';
import { Task, TaskStatus } from '../src/entities/task.entity';
define(Task, (fakerInstance) => {
const task = new Task();
task.id = fakerInstance.string.uuid();
task.title = fakerInstance.lorem.sentence({ min: 3, max: 8 });
task.description = fakerInstance.lorem.paragraph();
task.status = fakerInstance.helpers.arrayElement([
TaskStatus.TODO,
TaskStatus.IN_PROGRESS,
TaskStatus.DONE
]);
task.priority = fakerInstance.number.int({ min: 1, max: 5 });
task.dueDate = fakerInstance.date.future();
task.createdAt = fakerInstance.date.past();
task.updatedAt = new Date();
return task;
});
test/tasks.service.spec.ts:
import { Test } from '@nestjs/testing';
import { FactoryModule, factory } from 'typeorm-factories';
import { Task } from '../src/entities/task.entity';
describe('TasksService', () => {
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [FactoryModule],
// ... other providers
}).compile();
await module.init();
});
it('example test', async () => {
const task = await factory(Task).make({ priority: 5 });
expect(task.priority).toBe(5);
});
});
If you have questions or issues, please create an Issue in the project repository.
MIT