Mongoose Test Factory 🏭

The most intelligent and powerful test data generator for Mongoose. Generate realistic, valid, and contextually-aware test data with zero configuration OR take complete control with 40+ explicit factory types. Stop writing tedious mock objects and start building meaningful test data in seconds.
✨ Why Choose Mongoose Test Factory?
🚀 Zero Configuration Magic
- Instant Setup: Apply one plugin and immediately get intelligent data generation
- Schema-Aware: Automatically respects all your Mongoose validators, types, and constraints
- Semantic Intelligence: Recognizes field names like
email
, phoneNumber
, firstName
and generates appropriate realistic data
🧠 Intelligent by Design
- Deep Schema Analysis: Understands complex nested schemas, arrays, and subdocuments
- Explicit Type Control: Use
factoryType
for precise data generation (NEW!)
- Context-Aware Generation: Field names determine data type (e.g.,
userEmail
generates emails, firstName
generates names)
- Validation Compliant: Honors
required
, min
, max
, enum
, unique
, and custom validators
⚡ Three Generation Strategies
build()
: Lightning-fast plain JavaScript objects for unit tests
make()
: Mongoose instances with virtuals and methods for testing models
create()
: Fully persisted documents for integration tests
🔧 Production-Ready Features
- TypeScript First: Full type safety with intelligent autocompletion
- Explicit Factory Types: 40+ built-in types with
factoryType
specification
- Relationship Management: Automatic ObjectId generation and linking
- Global Configuration: Set seeds for reproducible tests across teams
- Performance Optimized: Batch operations, caching, and memory management
- Extensible: Custom generators, traits, and hooks for any use case
📦 Installation
npm install --save-dev mongoose-test-factory
yarn add --dev mongoose-test-factory
pnpm add --save-dev mongoose-test-factory
Note: mongoose
is a peer dependency and must be installed in your project.
🚀 Quick Start (30 seconds)
import mongoose, { Schema, Document } from 'mongoose';
import mongooseTestFactory, { withFactory } from 'mongoose-test-factory';
interface IUser extends Document {
name: string;
email: string;
age: number;
isActive: boolean;
}
const userSchema = new Schema<IUser>({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
age: { type: Number, min: 18, max: 120 },
isActive: { type: Boolean, default: true }
});
userSchema.plugin(mongooseTestFactory);
const UserModel = mongoose.model<IUser>('User', userSchema);
const User = withFactory(UserModel);
const user = User.factory().build();
console.log(user);
💡 TypeScript Tip: If you have custom model interfaces with static methods, use explicit type parameters: withFactory<IDocument, ICustomModel>(model)
to preserve all method types and get full IntelliSense support.
🎯 Explicit Factory Type Specification
NEW! Take complete control over data generation by specifying exactly what type of data you want for each field.
Basic Usage
const productSchema = new Schema({
email: { type: String, factoryType: 'email' },
firstName: { type: String, factoryType: 'firstName' },
lastName: { type: String, factoryType: 'lastName' },
price: { type: Number, factoryType: 'price' },
age: { type: Number, factoryType: 'age' },
tags: { type: [String], factoryType: 'tags' },
isActive: { type: Boolean, factoryType: 'active' },
isPremium: { type: Boolean, factoryType: 'premium' },
birthDate: { type: Date, factoryType: 'birthdate' },
lastLogin: { type: Date, factoryType: 'timestamp' },
userId: { type: ObjectId, factoryType: 'objectid' },
sessionId: { type: String, factoryType: 'uuid' }
});
E-commerce Example
const productSchema = new Schema({
name: { type: String, factoryType: 'title' },
description: { type: String, factoryType: 'description' },
price: { type: Number, factoryType: 'price' },
category: { type: String, factoryType: 'random', enum: ['electronics', 'clothing'] },
vendor: { type: String, factoryType: 'company' },
tags: { type: [String], factoryType: 'tags' },
isActive: { type: Boolean, factoryType: 'active' },
website: { type: String, factoryType: 'url' }
});
const product = Product.factory().build();
Priority System
- 🎯 Explicit factoryType (highest priority) - What you specify
- 🧠 Pattern matching (fallback) - Smart field name detection
- 🎲 Default generators (last resort) - Basic random data
📊 Factory Types Reference
String Types
email | Email addresses | user@example.com |
phone | Phone numbers | +1-555-123-4567 |
name | Full person names | John Doe |
firstName | First names only | John |
lastName | Last names only | Doe |
username | Usernames | john_doe123 |
password | Strong passwords | Str0ng!Pass |
url | Website URLs | https://example.com |
slug | URL slugs | my-blog-post |
address | Street addresses | 123 Main St |
city | City names | New York |
country | Country names | United States |
company | Company names | Tech Corp |
description | Text descriptions | Lorem ipsum dolor... |
title | Titles/headlines | Amazing Product Launch |
uuid | UUID strings | f47ac10b-58cc-4372... |
Number Types
price | Monetary values | 19.99 |
age | Human ages | 28 |
rating | Rating scores (1-5) | 4.2 |
percentage | Percentages (0-100) | 75 |
quantity | Product quantities | 150 |
year | Years | 2024 |
Date Types
timestamp | Current timestamps | 2024-01-15T10:30:00Z |
birthdate | Birth dates | 1990-05-15 |
futuredate | Future dates | 2025-06-20 |
pastdate | Past dates | 2023-01-10 |
Boolean Types
active | Active/enabled states | 80% |
verified | Verified/confirmed | 70% |
premium | Premium features | 25% |
public | Public visibility | 85% |
Array Types
tags | Tech/category tags | ['javascript', 'nodejs'] |
skills | Professional skills | ['Frontend Development'] |
emails | Array of emails | ['user@example.com'] |
phones | Array of phone numbers | ['555-123-4567'] |
names | Array of names | ['John Doe', 'Jane Smith'] |
categories | Product categories | ['Electronics', 'Books'] |
languages | Language codes | ['en', 'es', 'fr'] |
urls | Array of URLs | ['https://example.com'] |
Special Types
objectid | MongoDB ObjectIds | 507f1f77bcf86cd799439011 |
uuid | UUIDs | f47ac10b-58cc-4372-a567... |
random | Context-appropriate random | Varies by field type |
🏗️ How It Works Under the Hood
1. Plugin Integration & Schema Registration
When you apply mongooseTestFactory
to your schema, the plugin:
- Registers the schema in the global factory registry
- Injects a static
factory()
method into your model
- Performs deep analysis of field types, validators, and constraints
2. Intelligent Field Analysis
The factory analyzes each field in your schema:
{
email: { type: String, required: true, unique: true },
}
3. Semantic Pattern Recognition
Built-in field name patterns automatically generate appropriate data:
email
, userEmail
, contactEmail
→ realistic email addresses
firstName
, lastName
, fullName
→ person names
phoneNumber
, mobile
, phone
→ phone numbers
price
, cost
, amount
→ monetary values
createdAt
, updatedAt
→ timestamps
4. Three-Tier Generation Strategy
const data = User.factory().build();
const instance = User.factory().make();
const saved = await User.factory().create();
📚 Comprehensive Examples
Basic Usage Examples
Single Document Generation
const user = User.factory().build();
const admin = User.factory()
.with({ name: 'Admin User', role: 'admin' })
.build();
const savedUser = await User.factory().create();
Multiple Document Generation
const users = User.factory(10).build();
const users = User.factory().count(10).build();
const savedUsers = await User.factory(50).create();
Field Overrides
const user = User.factory()
.with('email', 'specific@example.com')
.build();
const user = User.factory()
.with({
name: 'John Smith',
age: 30,
isActive: false
})
.build();
const user = User.factory()
.with({
profile: {
bio: 'Software engineer',
preferences: { theme: 'dark' }
}
})
.build();
Relationship Examples
One-to-Many Relationships
interface IPost extends Document {
title: string;
content: string;
author: Types.ObjectId;
tags: string[];
}
const postSchema = new Schema<IPost>({
title: { type: String, required: true },
content: { type: String, required: true },
author: { type: Schema.Types.ObjectId, ref: 'User', required: true },
tags: [{ type: String }]
});
postSchema.plugin(mongooseTestFactory);
const Post = withFactory(mongoose.model<IPost>('Post', postSchema));
const author = await User.factory().create();
const posts = await Post.factory(5)
.with({ author: author._id })
.create();
console.log(posts[0].author.toString() === author._id.toString());
Many-to-Many Relationships
interface ITeam extends Document {
name: string;
members: Types.ObjectId[];
createdBy: Types.ObjectId;
}
const teamSchema = new Schema<ITeam>({
name: { type: String, required: true },
members: [{ type: Schema.Types.ObjectId, ref: 'User' }],
createdBy: { type: Schema.Types.ObjectId, ref: 'User', required: true }
});
teamSchema.plugin(mongooseTestFactory);
const Team = withFactory(mongoose.model<ITeam>('Team', teamSchema));
const users = await User.factory(5).create();
const creator = users[0];
const members = users.slice(1);
const team = await Team.factory()
.with({
createdBy: creator._id,
members: members.map(u => u._id)
})
.create();
Complex Schema Examples
E-commerce Product Schema
interface IProduct extends Document {
name: string;
description: string;
price: number;
category: string;
inStock: boolean;
inventory: {
quantity: number;
warehouse: string;
lastRestocked: Date;
};
reviews: Array<{
userId: Types.ObjectId;
rating: number;
comment: string;
createdAt: Date;
}>;
tags: string[];
images: string[];
vendor: Types.ObjectId;
}
const productSchema = new Schema<IProduct>({
name: { type: String, required: true, maxlength: 100 },
description: { type: String, required: true, maxlength: 1000 },
price: { type: Number, required: true, min: 0.01, max: 99999.99 },
category: {
type: String,
required: true,
enum: ['Electronics', 'Clothing', 'Books', 'Home', 'Sports']
},
inStock: { type: Boolean, default: true },
inventory: {
quantity: { type: Number, required: true, min: 0 },
warehouse: { type: String, required: true },
lastRestocked: { type: Date, default: Date.now }
},
reviews: [{
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
rating: { type: Number, required: true, min: 1, max: 5 },
comment: { type: String, maxlength: 500 },
createdAt: { type: Date, default: Date.now }
}],
tags: [{ type: String }],
images: [{ type: String }],
vendor: { type: Schema.Types.ObjectId, ref: 'Vendor', required: true }
});
productSchema.plugin(mongooseTestFactory);
const Product = withFactory(mongoose.model<IProduct>('Product', productSchema));
const products = Product.factory(10).build();
User Profile with Nested Data
interface IUserProfile extends Document {
personalInfo: {
firstName: string;
lastName: string;
dateOfBirth: Date;
gender: 'male' | 'female' | 'other';
};
contactInfo: {
email: string;
phoneNumber: string;
address: {
street: string;
city: string;
state: string;
zipCode: string;
country: string;
};
};
preferences: {
language: string;
timezone: string;
notifications: {
email: boolean;
sms: boolean;
push: boolean;
};
};
socialMedia: {
twitter?: string;
linkedin?: string;
github?: string;
};
metadata: {
createdAt: Date;
lastLoginAt: Date;
loginCount: number;
isVerified: boolean;
};
}
const userProfileSchema = new Schema<IUserProfile>({
personalInfo: {
firstName: { type: String, required: true, maxlength: 50 },
lastName: { type: String, required: true, maxlength: 50 },
dateOfBirth: { type: Date, required: true },
gender: { type: String, enum: ['male', 'female', 'other'], required: true }
},
contactInfo: {
email: { type: String, required: true, unique: true },
phoneNumber: { type: String, required: true },
address: {
street: { type: String, required: true },
city: { type: String, required: true },
state: { type: String, required: true },
zipCode: { type: String, required: true },
country: { type: String, required: true }
}
},
preferences: {
language: { type: String, default: 'en' },
timezone: { type: String, default: 'UTC' },
notifications: {
email: { type: Boolean, default: true },
sms: { type: Boolean, default: false },
push: { type: Boolean, default: true }
}
},
socialMedia: {
twitter: { type: String },
linkedin: { type: String },
github: { type: String }
},
metadata: {
createdAt: { type: Date, default: Date.now },
lastLoginAt: { type: Date, default: Date.now },
loginCount: { type: Number, default: 0, min: 0 },
isVerified: { type: Boolean, default: false }
}
});
userProfileSchema.plugin(mongooseTestFactory);
const UserProfile = withFactory(mongoose.model<IUserProfile>('UserProfile', userProfileSchema));
const profile = UserProfile.factory().build();
⚙️ Complete Configuration Guide
Global Configuration
import { FactoryPlugin } from 'mongoose-test-factory';
await FactoryPlugin.initialize({
seed: 12345,
locale: 'en_US',
debug: true,
factory: {
defaultBatchSize: 100,
enableMetrics: true,
enableCaching: true,
cacheSize: 1000,
validateByDefault: true,
defaultTraits: []
},
performance: {
enableCaching: true,
cacheSize: 1000,
enableBatching: true,
batchSize: 100,
enablePooling: false,
poolSize: 10
},
schemaAnalysis: {
enablePatternRecognition: true,
enableSemanticAnalysis: true,
customPatterns: {
sku: /^(sku|productCode|itemCode)$/i,
isbn: /^isbn/i
},
customSemantics: {
sku: () => `SKU-${Date.now()}`,
isbn: () => `978-${Math.random().toString().slice(2, 12)}`
}
},
autoRelations: true,
relationMappings: {
'User.posts': {
model: 'Post',
foreignKey: 'author',
defaultCount: 3
}
},
maxBatchSize: 10000
});
Advanced Factory Configuration
import { createFactory, FactoryHelpers } from 'mongoose-test-factory';
const userFactory = createFactory(User, {
defaults: {
isActive: true,
role: 'user',
createdAt: new Date()
},
traits: {
admin: (builder) => builder.with({
role: 'admin',
permissions: ['read', 'write', 'delete']
}),
verified: (builder) => builder.with({
emailVerified: true,
phoneVerified: true,
verifiedAt: new Date()
}),
premium: (builder) => builder.with({
subscription: 'premium',
subscriptionExpires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)
}),
inactive: (builder) => builder.with({
isActive: false,
deactivatedAt: new Date(),
lastLoginAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
})
},
hooks: {
beforeCreate: async (doc) => {
if (doc.password) {
doc.password = await hashPassword(doc.password);
}
},
afterCreate: async (doc) => {
await sendWelcomeEmail(doc.email);
},
beforeBuild: (doc) => {
doc.displayName = `${doc.firstName} ${doc.lastName}`;
},
afterBuild: (doc) => {
doc.slug = doc.name.toLowerCase().replace(/\s+/g, '-');
}
}
});
const admin = userFactory().trait('admin').trait('verified').build();
const premiumUsers = await userFactory(10).trait('premium').create();
🎯 Real-World Use Cases
Testing API Endpoints
describe('User API', () => {
beforeEach(async () => {
FactoryPlugin.setSeed(12345);
});
it('should create user with valid data', async () => {
const userData = User.factory().build();
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(response.body.email).toBe(userData.email);
});
it('should handle user registration', async () => {
const newUser = User.factory()
.with({ password: 'SecurePass123!' })
.build();
const response = await request(app)
.post('/api/register')
.send(newUser)
.expect(201);
expect(response.body.user.isActive).toBe(true);
});
});
Database Seeding
import { FactoryPlugin } from 'mongoose-test-factory';
async function seedDatabase() {
const admins = await User.factory(3)
.trait('admin')
.trait('verified')
.create();
const users = await User.factory(50)
.trait('verified')
.create();
for (const admin of admins) {
const products = await Product.factory(20)
.with({ vendor: admin._id })
.create();
for (const product of products) {
const reviewCount = Math.floor(Math.random() * 10) + 1;
const randomUsers = users.sort(() => 0.5 - Math.random()).slice(0, reviewCount);
product.reviews = randomUsers.map(user => ({
userId: user._id,
rating: Math.floor(Math.random() * 5) + 1,
comment: faker.lorem.paragraph(),
createdAt: faker.date.past()
}));
await product.save();
}
}
console.log('Database seeded successfully!');
}
Performance Testing
describe('Performance Tests', () => {
it('should handle bulk user creation', async () => {
const start = Date.now();
const users = await User.factory(10000).create();
const duration = Date.now() - start;
console.log(`Created ${users.length} users in ${duration}ms`);
expect(users).toHaveLength(10000);
expect(duration).toBeLessThan(30000);
});
it('should generate complex nested data efficiently', async () => {
const profiles = UserProfile.factory(1000).build();
expect(profiles).toHaveLength(1000);
expect(profiles.every(p => p.contactInfo.email.includes('@'))).toBe(true);
});
});
Integration Testing
describe('E-commerce Integration', () => {
let user: IUser;
let products: IProduct[];
beforeEach(async () => {
user = await User.factory()
.trait('verified')
.with({ balance: 1000 })
.create();
products = await Product.factory(5)
.with({ price: 50, inStock: true })
.create();
});
it('should complete purchase workflow', async () => {
const cartItems = products.slice(0, 3).map(p => ({
productId: p._id,
quantity: 1,
price: p.price
}));
const order = await Order.factory()
.with({
userId: user._id,
items: cartItems,
totalAmount: cartItems.reduce((sum, item) => sum + item.price, 0)
})
.create();
const payment = await Payment.factory()
.with({
orderId: order._id,
userId: user._id,
amount: order.totalAmount
})
.create();
expect(order.status).toBe('pending');
expect(payment.status).toBe('completed');
});
});
🔧 TypeScript Integration
Full Type Safety
import { withFactory, ModelWithFactory } from 'mongoose-test-factory';
const User: ModelWithFactory<IUser> = withFactory(
mongoose.model<IUser>('User', userSchema)
);
const user = User.factory()
.with({ name: 'John' })
.with({ invalidField: 'test' })
.build();
const users = User.factory(10)
.with({ isActive: true })
.trait('verified')
.build();
Custom Generator Types
declare module 'mongoose-test-factory' {
interface CustomGenerators {
isbn(): string;
sku(): string;
colorHex(): string;
}
}
const bookSchema = new Schema({
isbn: { type: String, required: true },
title: { type: String, required: true }
});
📊 Performance & Metrics
Built-in Performance Monitoring
await FactoryPlugin.initialize({
factory: { enableMetrics: true }
});
await User.factory(1000).create();
const metrics = FactoryPlugin.getMetrics();
console.log(metrics);
Memory Management
await FactoryPlugin.initialize({
performance: {
enableBatching: true,
batchSize: 100,
enablePooling: true,
poolSize: 50
}
});
🚨 Troubleshooting
Common Issues
TypeScript Errors
const user = User.factory().build();
import { withFactory } from 'mongoose-test-factory';
const User = withFactory(UserModel);
Custom Model Interfaces
When using custom model interfaces with static methods, you need to provide explicit type parameters to preserve all method types:
interface IProductModel extends mongoose.Model<IProduct> {
findByAnyId(id: string): Promise<IProduct | null>;
findByCategory(category: string): Promise<IProduct[]>;
}
const ProductModel = mongoose.model<IProduct, IProductModel>('Product', productSchema);
const Product = withFactory(ProductModel);
const Product = withFactory<IProduct, IProductModel>(ProductModel);
const product = Product.factory().build();
const byId = await Product.findByAnyId('...');
const products = await Product.findByCategory('...');
const standard = await Product.findById('...');
Validation Errors
const user = User.factory().build();
const userSchema = new Schema({
email: {
type: String,
required: true,
match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
}
});
Performance Issues
await User.factory(10000).create();
await FactoryPlugin.initialize({
factory: { defaultBatchSize: 500 },
performance: { enableBatching: true }
});
Debug Mode
await FactoryPlugin.initialize({ debug: true });
const user = User.factory().build();
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
git clone https://github.com/nexus-aissam/mongoose-test-factory.git
cd mongoose-test-factory
npm install
npm run build
npm test
📄 License
MIT License - see LICENSE file for details.
🙏 Acknowledgments
- Faker.js for realistic data generation
- Mongoose for MongoDB object modeling
- The TypeScript community for excellent tooling
Made with ❤️ by developers, for developers. Happy testing! 🚀