Testing with nanofaker
Learn how to use nanofaker effectively in your test suites for unit tests, integration tests, and end-to-end tests.
Why Use nanofaker for Testing?
- Realistic data - Generate data that looks authentic
- Reproducible - Use seeding for consistent test results
- Fast - Generate thousands of records quickly
- Comprehensive - 16 data categories cover most testing needs
- Type-safe - Full TypeScript support catches errors early
Unit Testing
Basic Example
ts
import { describe, expect, test } from 'bun:test'
import { faker } from 'nanofaker'
describe('User validation', () => {
test('validates email format', () => {
const email = faker.internet.email()
const isValid = validateEmail(email)
expect(isValid).toBe(true)
})
test('validates name length', () => {
const name = faker.person.fullName()
const isValid = name.length >= 2 && name.length <= 100
expect(isValid).toBe(true)
})
})
Seeded Tests for Consistency
ts
import { beforeEach, describe, expect, test } from 'bun:test'
import { faker } from 'nanofaker'
describe('User service', () => {
beforeEach(() => {
// Reset to known state before each test
faker.seed(1000)
})
test('creates user with valid data', () => {
const userData = {
name: faker.person.fullName(),
email: faker.internet.email(),
}
const user = createUser(userData)
expect(user.name).toBe('John Doe') // Always the same with seed 1000
})
test('rejects invalid email', () => {
const userData = {
name: faker.person.fullName(),
email: 'invalid-email',
}
expect(() => createUser(userData)).toThrow()
})
})
Integration Testing
Database Testing
ts
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
import { faker } from 'nanofaker'
describe('User repository', () => {
let db: Database
beforeEach(async () => {
db = await setupTestDatabase()
})
afterEach(async () => {
await cleanupTestDatabase(db)
})
test('saves and retrieves user', async () => {
const user = {
name: faker.person.fullName(),
email: faker.internet.email(),
city: faker.address.city(),
}
await db.users.create(user)
const retrieved = await db.users.findByEmail(user.email)
expect(retrieved?.name).toBe(user.name)
})
test('handles bulk insert', async () => {
const users = Array.from({ length: 100 }, () => ({
name: faker.person.fullName(),
email: faker.internet.email(),
city: faker.address.city(),
}))
await db.users.bulkCreate(users)
const count = await db.users.count()
expect(count).toBe(100)
})
})
API Testing
ts
import { describe, expect, test } from 'bun:test'
import { faker } from 'nanofaker'
describe('User API', () => {
test('POST /users creates new user', async () => {
const userData = {
name: faker.person.fullName(),
email: faker.internet.email(),
password: 'SecurePass123!',
}
const response = await fetch('http://localhost:3000/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData),
})
expect(response.status).toBe(201)
const data = await response.json()
expect(data.name).toBe(userData.name)
})
test('GET /users returns user list', async () => {
// Create test users
const users = Array.from({ length: 5 }, () => ({
name: faker.person.fullName(),
email: faker.internet.email(),
}))
for (const user of users) {
await createUser(user)
}
const response = await fetch('http://localhost:3000/users')
const data = await response.json()
expect(data.length).toBeGreaterThanOrEqual(5)
})
})
Test Fixtures
Creating Reusable Fixtures
ts
import { faker } from 'nanofaker'
export class UserFixture {
static create(overrides: Partial<User> = {}): User {
return {
id: crypto.randomUUID(),
name: faker.person.fullName(),
email: faker.internet.email(),
city: faker.address.city(),
createdAt: new Date(),
...overrides,
}
}
static createMany(count: number, overrides: Partial<User> = {}): User[] {
return Array.from({ length: count }, () => this.create(overrides))
}
static createWithLocale(locale: string): User {
faker.locale = locale as any
return this.create()
}
}
// Usage in tests
test('user service', () => {
const user = UserFixture.create({ email: 'test@example.com' })
const users = UserFixture.createMany(10)
const spanishUser = UserFixture.createWithLocale('es')
})
Factory Builder Pattern
ts
import { faker } from 'nanofaker'
class ProductBuilder {
private product: Partial<Product> = {
id: crypto.randomUUID(),
name: faker.commerce.product(),
price: Math.random() * 1000,
category: faker.commerce.department(),
}
withName(name: string): this {
this.product.name = name
return this
}
withPrice(price: number): this {
this.product.price = price
return this
}
withCategory(category: string): this {
this.product.category = category
return this
}
build(): Product {
return this.product as Product
}
}
// Usage
test('product pricing', () => {
const product = new ProductBuilder()
.withName('Laptop')
.withPrice(999.99)
.build()
expect(calculateTax(product)).toBe(99.99)
})
Snapshot Testing
Basic Snapshots
ts
import { expect, test } from 'bun:test'
import { faker } from 'nanofaker'
test('user profile matches snapshot', () => {
faker.seed(42) // Always use same seed for snapshots
const profile = {
name: faker.person.fullName(),
email: faker.internet.email(),
bio: `${faker.person.jobTitle()} from ${faker.address.city()}`,
}
expect(profile).toMatchSnapshot()
})
Dynamic Snapshots
ts
import { expect, test } from 'bun:test'
import { faker } from 'nanofaker'
test('generates consistent user list', () => {
faker.seed(100)
const users = Array.from({ length: 5 }, () => ({
name: faker.person.fullName(),
email: faker.internet.email(),
}))
expect(users).toMatchSnapshot()
})
Performance Testing
Load Testing
ts
import { test } from 'bun:test'
import { faker } from 'nanofaker'
test('handles 10,000 user creation', async () => {
const startTime = performance.now()
const users = Array.from({ length: 10000 }, () => ({
name: faker.person.fullName(),
email: faker.internet.email(),
city: faker.address.city(),
}))
const endTime = performance.now()
const duration = endTime - startTime
console.log(`Generated 10,000 users in ${duration}ms`)
expect(duration).toBeLessThan(500) // Should be fast
})
Memory Testing
ts
import { test } from 'bun:test'
import { faker } from 'nanofaker'
test('memory usage remains stable', () => {
const initialMemory = process.memoryUsage().heapUsed
// Generate large dataset
for (let i = 0; i < 100000; i++) {
faker.person.fullName()
}
const finalMemory = process.memoryUsage().heapUsed
const memoryIncrease = finalMemory - initialMemory
// Memory increase should be minimal
expect(memoryIncrease).toBeLessThan(50 * 1024 * 1024) // Less than 50MB
})
Edge Cases
Testing with Edge Data
ts
import { expect, test } from 'bun:test'
import { faker } from 'nanofaker'
test('handles various name formats', () => {
faker.seed(1)
const names = Array.from({ length: 100 }, () =>
faker.person.fullName())
// Test that all names are valid
names.forEach((name) => {
expect(name.length).toBeGreaterThan(0)
expect(name).not.toContain('undefined')
expect(name).not.toContain('null')
})
})
test('handles all locales', () => {
const locales = ['en', 'es', 'fr', 'de', 'it', 'pt', 'ja', 'tl', 'zh']
locales.forEach((locale) => {
faker.locale = locale as any
const name = faker.person.fullName()
expect(name).toBeTruthy()
expect(name.length).toBeGreaterThan(0)
})
})
Mocking with nanofaker
Mock API Responses
ts
import { faker } from 'nanofaker'
function mockUserAPI() {
return {
getUser: async (id: string) => ({
id,
name: faker.person.fullName(),
email: faker.internet.email(),
}),
getUsers: async () =>
Array.from({ length: 10 }, (_, i) => ({
id: `user-${i}`,
name: faker.person.fullName(),
email: faker.internet.email(),
})),
}
}
test('component displays user data', async () => {
const api = mockUserAPI()
const user = await api.getUser('123')
expect(user.name).toBeTruthy()
expect(user.email).toContain('@')
})
Best Practices
1. Use Seeds for Reproducibility
ts
// Good - Consistent results
beforeEach(() => {
faker.seed(1000)
})
// Avoid - Random results every time
// (no seeding)
2. Create Reusable Fixtures
ts
// Good - Reusable across tests
const userFixture = {
create: () => ({
name: faker.person.fullName(),
email: faker.internet.email(),
}),
}
// Avoid - Duplicating faker calls everywhere
3. Clean Up After Tests
ts
afterEach(async () => {
await cleanupDatabase()
faker.seed(undefined) // Reset faker state
})
4. Test with Multiple Locales
ts
test('handles international users', () => {
['en', 'es', 'ja'].forEach((locale) => {
faker.locale = locale as any
const user = createUser()
expect(validateUser(user)).toBe(true)
})
})
5. Combine with Property-Based Testing
ts
import { faker } from 'nanofaker'
test('email validation works for all generated emails', () => {
const emails = Array.from({ length: 100 }, () =>
faker.internet.email())
emails.forEach((email) => {
expect(validateEmail(email)).toBe(true)
})
})
nanofaker makes testing easier by providing realistic, reproducible, and comprehensive test data across all your testing needs.