Test
Test your Business-as-Code with comprehensive unit, integration, and end-to-end testing strategies
Test your Business-as-Code with comprehensive unit, integration, and end-to-end testing strategies to ensure reliability and correctness.
Overview
The Test phase ensures your Business-as-Code works correctly, reliably, and at scale. Using modern testing frameworks and patterns, you validate business logic, workflows, integrations, and user experiences before deployment.
This phase emphasizes:
- Unit Testing: Test individual components in isolation
- Integration Testing: Test component interactions
- End-to-End Testing: Test complete user workflows
- Contract Testing: Verify API contracts
- Performance Testing: Validate scalability and speed
Testing Primitives
Unit Tests
Test individual business logic components:
import { describe, it, expect, beforeEach } from 'vitest'
import { db } from '@dotdo/platform'
describe('Order Processing', () => {
beforeEach(async () => {
await db.reset() // Reset test database
})
it('calculates order total correctly', async () => {
const order = await db.create($.Order, {
items: [
{ product: 'Widget', price: 10, quantity: 2 },
{ product: 'Gadget', price: 15, quantity: 1 },
],
})
const total = calculateOrderTotal(order)
expect(total).toBe(35) // (10 * 2) + (15 * 1)
})
it('applies discount correctly', async () => {
const order = await db.create($.Order, {
items: [{ product: 'Widget', price: 100, quantity: 1 }],
discount: { type: 'percentage', value: 20 },
})
const total = calculateOrderTotal(order)
expect(total).toBe(80) // 100 - 20%
})
it('handles tax calculation', async () => {
const order = await db.create($.Order, {
items: [{ product: 'Widget', price: 100, quantity: 1 }],
taxRate: 0.08,
})
const total = calculateOrderTotal(order)
expect(total).toBe(108) // 100 + 8% tax
})
})Integration Tests
Test component interactions and workflows:
describe('Checkout Workflow', () => {
it('completes full checkout process', async () => {
// 1. Create user
const user = await db.create($.User, {
email: '[email protected]',
name: 'Test User',
})
// 2. Add items to cart
const cart = await db.create($.Cart, {
userId: user.id,
items: [{ productId: 'prod_123', quantity: 2 }],
})
// 3. Process checkout
const order = await checkout({
userId: user.id,
cartId: cart.id,
paymentMethod: 'test_card',
})
// 4. Verify order created
expect(order.status).toBe('pending_payment')
// 5. Verify inventory reserved
const product = await db.get($.Product, 'prod_123')
expect(product.reserved).toBe(2)
// 6. Verify email sent
const emails = await getTestEmails()
expect(emails).toHaveLength(1)
expect(emails[0].to).toBe(user.email)
expect(emails[0].template).toBe('order-confirmation')
})
it('handles payment failure gracefully', async () => {
const user = await createTestUser()
const cart = await createTestCart(user.id)
// Use failing payment method
const result = await checkout({
userId: user.id,
cartId: cart.id,
paymentMethod: 'fail_card',
})
// Verify order not created
expect(result.success).toBe(false)
expect(result.error).toBe('payment_failed')
// Verify inventory not reserved
const product = await db.get($.Product, cart.items[0].productId)
expect(product.reserved).toBe(0)
// Verify failure email sent
const emails = await getTestEmails()
expect(emails[0].template).toBe('payment-failed')
})
})End-to-End Tests
Test complete user workflows:
import { test, expect } from '@playwright/test'
test.describe('User Signup Flow', () => {
test('new user can signup and activate account', async ({ page }) => {
// 1. Visit signup page
await page.goto('/signup')
// 2. Fill signup form
await page.fill('[name="email"]', '[email protected]')
await page.fill('[name="password"]', 'SecurePassword123!')
await page.fill('[name="name"]', 'New User')
// 3. Submit form
await page.click('button[type="submit"]')
// 4. Verify redirect to onboarding
await expect(page).toHaveURL('/onboarding')
// 5. Verify welcome message
await expect(page.locator('h1')).toContainText('Welcome, New User')
// 6. Verify account created in database
const user = await db.findOne($.User, {
where: { email: '[email protected]' },
})
expect(user).toBeDefined()
expect(user.status).toBe('active')
// 7. Verify welcome email sent
const email = await getLatestEmail('[email protected]')
expect(email.subject).toContain('Welcome')
})
test('user can complete onboarding', async ({ page }) => {
const user = await createTestUser({ onboarded: false })
await loginAs(page, user)
// Navigate through onboarding steps
await page.goto('/onboarding')
// Step 1: Profile
await page.fill('[name="company"]', 'Acme Corp')
await page.click('button:has-text("Next")')
// Step 2: Preferences
await page.check('[name="emailNotifications"]')
await page.click('button:has-text("Next")')
// Step 3: Complete
await page.click('button:has-text("Get Started")')
// Verify redirect to dashboard
await expect(page).toHaveURL('/dashboard')
// Verify onboarding completed
const updated = await db.get($.User, user.id)
expect(updated.onboardingCompleted).toBe(true)
})
})Contract Testing
Verify API contracts between services:
import { pactWith } from 'jest-pact'
pactWith({ consumer: 'Frontend', provider: 'API' }, (interaction) => {
interaction('get user profile', () => ({
state: 'user exists',
uponReceiving: 'a request for user profile',
withRequest: {
method: 'GET',
path: '/api/users/123',
headers: {
Authorization: 'Bearer token',
},
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json',
},
body: {
id: '123',
email: '[email protected]',
name: 'Test User',
plan: 'pro',
},
},
}))
test('fetches user profile', async () => {
const response = await fetch('/api/users/123', {
headers: { Authorization: 'Bearer token' },
})
const user = await response.json()
expect(user.email).toBe('[email protected]')
})
})Performance Testing
Validate system performance:
import { test } from 'vitest'
import autocannon from 'autocannon'
describe('API Performance', () => {
test('handles 1000 requests/second', async () => {
const result = await autocannon({
url: 'http://localhost:3000/api/products',
connections: 100,
duration: 30, // 30 seconds
pipelining: 10,
})
// Verify performance metrics
expect(result.requests.average).toBeGreaterThan(1000)
expect(result.latency.p95).toBeLessThan(100) // 95th percentile < 100ms
expect(result.errors).toBe(0)
})
test('database query performance', async () => {
const iterations = 1000
const start = Date.now()
for (let i = 0; i < iterations; i++) {
await db.list($.Product, { limit: 10 })
}
const duration = Date.now() - start
const avgLatency = duration / iterations
expect(avgLatency).toBeLessThan(10) // Average < 10ms per query
})
})Testing Patterns
Test Data Builders
Create reusable test data:
// Test data builders
export const TestBuilders = {
user: (overrides = {}) => ({
email: `test-${Date.now()}@example.com`,
name: 'Test User',
status: 'active',
...overrides,
}),
order: (overrides = {}) => ({
userId: 'user_123',
items: [{ productId: 'prod_123', quantity: 1, price: 10 }],
status: 'pending',
total: 10,
...overrides,
}),
product: (overrides = {}) => ({
name: 'Test Product',
price: 99,
category: 'Test',
inventory: 100,
...overrides,
}),
}
// Usage
const user = await db.create($.User, TestBuilders.user({ plan: 'pro' }))
const order = await db.create($.Order, TestBuilders.order({ userId: user.id }))Test Fixtures
Manage complex test setups:
export async function setupEcommerceFixture() {
// Create products
const products = await Promise.all([
db.create($.Product, { name: 'Widget', price: 10, inventory: 100 }),
db.create($.Product, { name: 'Gadget', price: 20, inventory: 50 }),
])
// Create users
const users = await Promise.all([
db.create($.User, { email: '[email protected]', plan: 'free' }),
db.create($.User, { email: '[email protected]', plan: 'pro' }),
])
// Create orders
const orders = await Promise.all([
db.create($.Order, {
userId: users[0].id,
items: [{ productId: products[0].id, quantity: 2 }],
}),
])
return { products, users, orders }
}
// Usage
describe('Ecommerce Features', () => {
let fixture
beforeEach(async () => {
fixture = await setupEcommerceFixture()
})
it('processes refund', async () => {
const refund = await processRefund(fixture.orders[0].id)
expect(refund.status).toBe('completed')
})
})Best Practices
Do's
- Test behavior, not implementation - Test what code does, not how
- Write tests first - TDD ensures testable code
- Keep tests fast - Fast tests run frequently
- Test edge cases - Don't just test happy paths
- Use factories - Reusable test data builders
- Mock external services - Isolate system under test
- Test at appropriate level - Unit for logic, E2E for workflows
Don'ts
- Don't test implementation details - Tests become brittle
- Don't skip tests - Untested code breaks
- Don't test third-party code - Trust libraries
- Don't use production data - Use test fixtures
- Don't ignore flaky tests - Fix or remove them
- Don't over-mock - Integration tests need real interactions
CLI Tools
# Run all tests
do test
# Run specific test file
do test src/orders.test.ts
# Run tests in watch mode
do test:watch
# Run integration tests
do test:integration
# Run E2E tests
do test:e2e
# Generate coverage report
do test:coverage
# Run performance tests
do test:performanceNext Steps
- Deploy → - Deploy tested code
- Evaluate → - Evaluate quality metrics
- Manage → - Monitor in production
Testing Tip: The best tests are fast, focused, and fail for one reason. Write tests that document behavior.