The pnpm fixture command manages consistent data sets for testing. You can copy actual data from the development environment to the test environment for stable and repeatable tests.
Basic Concept
Fixture is a fixed data set for testing:
- Consistency: All tests start with the same initial data
- Isolation: Separate test DB from development DB
- Reproducibility: Repeatable tests under same conditions
- Relationship preservation: Foreign key relationships maintained
Commands
init - Initialize Test DB
Copy development DB schema to test DB.
Execution process:
DUMP...
✓ Schema dumped from development_master
SYNC to (REMOTE) Fixture DB...
✓ Database myapp_fixture created
✓ Schema applied
SYNC to (LOCAL) Testing DB...
✓ Database myapp_test created
✓ Schema applied
Fixture initialization completed!
Databases created:
- Fixture DB (remote): Shared fixture storage
- Test DB (local): Local testing DB
init copies schema only. Data is not included.
import - Import Data
Save specific records from development DB as fixtures.
Interactive prompt appears:
? Please select entity: (Use arrow keys)
❯ User
Post
Comment
? Enter record IDs (comma-separated): 1,2,3
Importing fixtures...
✓ Imported User #1
✓ Imported User #2
✓ Imported User #3
✓ Imported related records (5 dependencies)
Fixture import completed!
Related data automatically included:
- Records referenced by foreign keys
- Relationship table records
- Reverse reference records (optional)
sync - Synchronize
Apply saved fixtures to test DB.
Execution process:
Syncing fixtures...
Clearing test database...
✓ Cleared all tables
Applying fixtures...
✓ Applied 3 users
✓ Applied 7 posts
✓ Applied 12 comments
✓ Applied 5 categories
Fixture sync completed!
Calling sync before test execution ensures you always start with a clean state.
gen - Auto Fixture Generation
Automatically generate fixtures using Entity’s cone metadata. Useful when you don’t have real data or need virtual test data.
Interactive mode:
? Select entities to generate fixtures for: (Use arrow keys)
❯ ◯ User
◯ Post
◯ Comment
? Number to generate per Entity: 5
? Save method: (Use arrow keys)
❯ Save to Fixture DB
Save to file (auto filename)
Save to file (specify filename)
Don't save (output only)
? Generate more realistic data with LLM? (fixtureHint-based, requires ANTHROPIC_API_KEY) No
CLI options:
| Option | Description | Example |
|---|
--all | Generate for all Entities | pnpm fixture gen --all |
--include | Include specific Entities only | pnpm fixture gen --include User,Post |
--exclude | Exclude specific Entities (use with —all) | pnpm fixture gen --all --exclude Comment |
--count | Number of records to generate | pnpm fixture gen --include User --count 10 |
--save-to | Specify save method | pnpm fixture gen --save-to db |
--use-llm | Generate realistic data with LLM (fixtureHint-based, requires ANTHROPIC_API_KEY) | pnpm fixture gen --use-llm |
--no-cache | Disable LLM cache (use with --use-llm) | pnpm fixture gen --use-llm --no-cache |
Save methods:
db: Save directly to Fixture DB (default)
file: Save to test/fixtures/{entity_table}.json file
file:{filename}: Save with specified filename
none: Don’t save, just output to console
# Example: Generate 10 each of User, Post and save to DB
pnpm fixture gen --include User,Post --count 10 --save-to db
fetch - Fetch Data from Production DB
Fetch data from actual production (or development) DB and save to Fixture DB. Use when you need realistic test data.
Interactive mode:
? Select entities to import: (Use arrow keys)
❯ ◯ User
◯ Post
◯ Comment
Importing User, Post...
✓ User: 10 records imported
✓ Post: 10 records imported
CLI options:
| Option | Description | Example |
|---|
--all | Fetch from all Entities | pnpm fixture fetch --all |
--include | Include specific Entities only | pnpm fixture fetch --include User,Post |
--exclude | Exclude specific Entities | pnpm fixture fetch --all --exclude Log |
--strategy | Data selection strategy | pnpm fixture fetch --strategy recent |
--limit | Number of records to fetch | pnpm fixture fetch --limit 20 |
Data selection strategies:
recent (default): Fetch most recent data first
sample: Random sampling
# Example: Fetch 20 most recent User records
pnpm fixture fetch --include User --strategy recent --limit 20
Features:
- Related data is automatically fetched together (up to depth level 2)
- Foreign key referential integrity maintained
explore - Query DB Data
Query only DB data. Useful for quickly checking data without saving.
Interactive mode:
? Entity to explore: User
User 10 records retrieved:
┌─────────┬────┬────────────────────┬───────────┐
│ (index) │ id │ email │ name │
├─────────┼────┼────────────────────┼───────────┤
│ 0 │ 1 │ 'user1@example.com'│ 'John' │
│ 1 │ 2 │ 'user2@example.com'│ 'Jane' │
│ ... │... │ ... │ ... │
└─────────┴────┴────────────────────┴───────────┘
CLI options:
| Option | Description | Example |
|---|
--include | Entity to explore | pnpm fixture explore --include User |
--strategy | Data selection strategy | pnpm fixture explore --strategy sample |
--limit | Number of records to query | pnpm fixture explore --limit 5 |
Data selection strategies:
sample (default): Random sample
recent: Most recent data
random: Completely random
ids: Specify specific IDs
query: Custom query
# Example: Query 5 most recent records from User table
pnpm fixture explore --include User --strategy recent --limit 5
Usage Workflow
1. Initial Setup
Run once when starting the project.
# Create test DB and copy schema
pnpm fixture init
2. Select Needed Data
Import actual data needed for testing.
# Import User #1, #2, #3
pnpm fixture import
# Entity: User
# IDs: 1,2,3
3. Write Tests
import { FixtureManager } from "sonamu/test";
beforeAll(async () => {
// Sync fixtures
await FixtureManager.sync();
});
test("Find User by email", async () => {
// Use User #1 from fixture
const user = await UserModel.findByEmail("user1@example.com");
expect(user).toBeDefined();
expect(user.id).toBe(1);
});
4. Run Tests
Fixtures are automatically synced each time tests run.
Fixture Files
Storage Location
📁src/
📁fixtures/
📄JSONusers.json - User fixture
📄JSONposts.json - Post fixture
📄JSONcomments.json - Comment fixture
[
{
"id": 1,
"email": "user1@example.com",
"name": "John Doe",
"created_at": "2024-01-15T00:00:00.000Z"
},
{
"id": 2,
"email": "user2@example.com",
"name": "Jane Smith",
"created_at": "2024-01-16T00:00:00.000Z"
}
]
Features:
- JSON format
- Separate files per Entity
- Related data automatically included
- Version controlled with Git
Relationship Data Handling
Automatic Foreign Key Inclusion
When importing a Post, connected Users are automatically included.
pnpm fixture import
# Entity: Post
# IDs: 1
# Automatically included:
# ✓ Post #1
# ✓ User #5 (author)
# ✓ Category #2 (category)
N:M Relationship Handling
Many-to-many relationship tables are also automatically included.
pnpm fixture import
# Entity: Post
# IDs: 1
# Automatically included:
# ✓ Post #1
# ✓ post_tags (junction table)
# ✓ Tag #1, #2, #3 (connected tags)
Test Isolation
Reset Before Each Test
describe("User CRUD", () => {
beforeEach(async () => {
// Reapply fixture before each test
await FixtureManager.sync();
});
test("Create user", async () => {
const user = await UserModel.create({
email: "new@example.com",
name: "New User",
});
expect(user.id).toBeDefined();
});
test("Delete user", async () => {
await UserModel.deleteById(1);
const user = await UserModel.findById(1);
expect(user).toBeNull();
});
});
Isolation effect:
- Each test is independent
- Test order doesn’t matter
- Parallel execution possible
Transaction Isolation
import { Sonamu } from "sonamu";
test("Transaction test", async () => {
await Sonamu.runScript(async () => {
// Execute within transaction
const user = await UserModel.create({...});
const post = await PostModel.create({...});
// Auto-rollback after test completes
});
});
Practical Examples
Complex Relationship Testing
describe("Post with Comments", () => {
beforeAll(async () => {
// Post #1 (+ User #1, Comments)
await FixtureManager.sync();
});
test("Get Post with comments", async () => {
const post = await PostModel.findById(1, {
include: ["comments"],
});
expect(post.comments).toHaveLength(3);
expect(post.comments[0].author_id).toBe(2);
});
});
Specific Scenario Data
describe("Premium User Features", () => {
beforeAll(async () => {
// Import only premium users
await FixtureManager.importFixture("User", [10, 11, 12]);
await FixtureManager.sync();
});
test("Premium feature access", async () => {
const user = await UserModel.findById(10);
expect(user.isPremium).toBe(true);
expect(await user.canAccessFeature("advanced")).toBe(true);
});
});
Troubleshooting
Fixture DB Connection Failed
Problem: Cannot access remote fixture DB
Error: connect ECONNREFUSED
Solution:
export default {
database: {
fixture: {
client: "pg",
connection: {
host: "fixture-db.example.com", // Correct host
database: "myapp_fixture",
user: "postgres",
password: process.env.FIXTURE_DB_PASSWORD,
},
},
},
};
Missing Relationship Data
Problem: Foreign key error occurs
Error: Foreign key constraint violation
Solution:
# Import related data first
pnpm fixture import
# Entity: User
# IDs: 1,2,3
pnpm fixture import
# Entity: Post
# IDs: 1,2,3 # References User 1,2,3
Fixture Conflict
Problem: ID duplication
Error: Duplicate entry '1' for key 'PRIMARY'
Solution:
# Reinitialize Test DB
pnpm fixture init
# Resync fixtures
pnpm fixture sync
Best Practices
1. Minimal Data
# ❌ Import all data
pnpm fixture import
# IDs: 1-1000
# ✅ Only necessary data
pnpm fixture import
# IDs: 1,2,3 # Representative cases only
2. Meaningful Data
// ✅ Data with clear test purpose
{
"id": 1,
"email": "admin@example.com",
"role": "admin",
"name": "Admin User"
}
{
"id": 2,
"email": "user@example.com",
"role": "user",
"name": "Regular User"
}
3. Version Control
# Include fixture files in Git
git add src/fixtures/
git commit -m "Update test fixtures"
4. CI/CD Integration
.github/workflows/test.yml
jobs:
test:
steps:
- name: Setup Database
run: |
pnpm fixture init
pnpm fixture sync
- name: Run Tests
run: pnpm test
Next Steps