| .. | ||
| utils | ||
| example.spec.ts | ||
| README.md | ||
| setup.ts | ||
Analytics Backend Test Infrastructure
This directory contains the test infrastructure for the analytics backend service.
Overview
The test infrastructure is built using Vitest with comprehensive utilities for mocking TypeORM repositories and creating test data.
Structure
test/
├── setup.ts # Global test setup (mocks, environment)
├── utils/
│ ├── mock-repository.ts # TypeORM repository mocking utilities
│ ├── factories.ts # Test entity factory functions
│ └── index.ts # Unified exports
├── example.spec.ts # Example tests (can be deleted)
└── README.md # This file
Running Tests
# Run all tests
npm run test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:cov
# Run E2E tests
npm run test:e2e
Coverage Targets
The test suite enforces the following coverage thresholds:
- Lines: 80%
- Functions: 80%
- Branches: 80%
- Statements: 80%
Using Factory Functions
Factory functions provide convenient ways to create test entities with sensible defaults:
import {
createMockContentView,
createMockRevenueMetric,
createMockEngagementMetric,
createMockPlatformError,
createMockABTest,
} from './utils'
// Create with defaults
const contentView = createMockContentView()
// Create with overrides
const customContentView = createMockContentView({
id: 'custom-id',
duration: 100,
userId: 'specific-user-id',
})
// Create multiple entities
const contentViews = createMockContentViews(5, { userId: 'same-user' })
Available Factories
- createMockContentView(overrides?) - Create ContentView entity
- createMockContentViews(count, overrides?) - Create multiple ContentView entities
- createMockRevenueMetric(overrides?) - Create RevenueMetric entity
- createMockRevenueMetrics(count, overrides?) - Create multiple RevenueMetric entities
- createMockEngagementMetric(overrides?) - Create EngagementMetric entity
- createMockEngagementMetrics(count, overrides?) - Create multiple EngagementMetric entities
- createMockPlatformError(overrides?) - Create PlatformError entity
- createMockPlatformErrors(count, overrides?) - Create multiple PlatformError entities
- createMockABTest(overrides?) - Create ABTest entity
- createMockABTests(count, overrides?) - Create multiple ABTest entities
- createMockABTestVariant(overrides?) - Create ABTestVariant
- createMockABTestResults(overrides?) - Create ABTestResults
- createDateOffset(days) - Create date offset from now
- createMockUUID() - Generate random UUID for testing
Using Mock Repositories
The mock repository utilities provide TypeORM-compatible mocks for testing services:
Basic Usage
import { createMockRepository } from './utils'
import { ContentView } from '@/entities/content-view.entity'
describe('ContentViewService', () => {
let mockRepo: MockRepository<ContentView>
beforeEach(() => {
mockRepo = createMockRepository<ContentView>()
})
it('should find content views', async () => {
const mockData = [createMockContentView()]
mockRepo.find.mockResolvedValue(mockData)
const result = await mockRepo.find()
expect(result).toEqual(mockData)
})
})
Using with NestJS Testing Module
import { Test } from '@nestjs/testing'
import { getRepositoryToken } from '@nestjs/typeorm'
import { createMockRepositoryProvider } from './utils'
import { ContentView } from '@/entities/content-view.entity'
import { ContentViewService } from '@/content-view/content-view.service'
describe('ContentViewService', () => {
let service: ContentViewService
let repo: MockedRepository<ContentView>
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
ContentViewService,
{
provide: getRepositoryToken(ContentView),
useValue: createMockRepositoryProvider<ContentView>(),
},
],
}).compile()
service = module.get<ContentViewService>(ContentViewService)
repo = module.get(getRepositoryToken(ContentView))
})
it('should be defined', () => {
expect(service).toBeDefined()
})
})
Query Builder Mocking
import { createMockRepository, createMockContentView } from './utils'
const mockRepo = createMockRepository<ContentView>()
const queryBuilder = mockRepo.getQueryBuilder()
const mockData = [createMockContentView()]
// Set expected result
queryBuilder.mockResult(mockData)
// Execute query
const result = await mockRepo
.createQueryBuilder('contentView')
.where('contentView.userId = :userId', { userId: 'test-user-id' })
.orderBy('contentView.createdAt', 'DESC')
.getMany()
expect(result).toEqual(mockData)
expect(queryBuilder.where).toHaveBeenCalledWith(
'contentView.userId = :userId',
{ userId: 'test-user-id' }
)
Available Repository Methods
The mock repository provides all standard TypeORM repository methods:
find(options?)- Find entitiesfindOne(options?)- Find single entityfindOneBy(criteria)- Find by criteriafindAndCount(options?)- Find with countsave(entity)- Save entitycreate(data)- Create entity instanceupdate(criteria, data)- Update entitiesdelete(criteria)- Delete entitiesremove(entity)- Remove entitycount(options?)- Count entitiesincrement(criteria, field, value)- Increment fielddecrement(criteria, field, value)- Decrement fieldcreateQueryBuilder(alias)- Create query builder
Query Builder Methods
The mock query builder supports chaining:
select()- Select fieldswhere()/andWhere()/orWhere()- Filter conditionsorderBy()/addOrderBy()- SortinggroupBy()/addGroupBy()- Groupingskip()/take()/limit()/offset()- PaginationleftJoin()/innerJoin()- JoinsgetOne()- Get single resultgetMany()- Get multiple resultsgetCount()- Get countgetManyAndCount()- Get results with countexecute()- Execute query
Mocked Dependencies
The test setup automatically mocks common dependencies:
Redis
import { mockRedis } from './setup'
// Redis is mocked globally
mockRedis.get.mockResolvedValue('cached-value')
mockRedis.set.mockResolvedValue('OK')
BullMQ
import { mockQueue } from './setup'
// Queue is mocked globally
mockQueue.add.mockResolvedValue({ id: '1', data: {} })
Test Organization
Follow this structure for test files:
import { describe, it, expect, beforeEach } from 'vitest'
import { Test } from '@nestjs/testing'
describe('ServiceName', () => {
// Setup
let service: ServiceName
beforeEach(async () => {
// Initialize test module
})
describe('methodName', () => {
it('should do something specific', async () => {
// Arrange
const input = { /* ... */ }
// Act
const result = await service.methodName(input)
// Assert
expect(result).toEqual(expectedOutput)
})
it('should handle error case', async () => {
// Test error scenarios
})
})
})
Best Practices
- Arrange-Act-Assert: Structure tests clearly with setup, execution, and verification
- Descriptive Names: Use clear, descriptive test names that explain what is being tested
- Isolation: Each test should be independent and not rely on other tests
- Reset Mocks: Use
beforeEachto reset mocks between tests - Type Safety: Use TypeScript types with factory functions and mocks
- Coverage: Aim for 80%+ coverage, but focus on meaningful tests over metrics
- Edge Cases: Test happy path, error cases, and edge conditions
Path Aliases
The test configuration supports path aliases matching tsconfig.json:
import { ContentView } from '@/entities/content-view.entity'
import { SomeService } from '@/some/some.service'
The @/ alias resolves to the src/ directory.
Environment Variables
Test environment variables are set in test/setup.ts:
NODE_ENV=testDATABASE_URL=postgresql://test:test@localhost:5432/analytics_testREDIS_URL=redis://localhost:6379JWT_SECRET=test-secret-key-for-testing-only
Override these in specific tests if needed.
Next Steps
- Delete
test/example.spec.tsonce you start writing actual tests - Create test files next to source files:
*.spec.tsor*.test.ts - Run tests with coverage to identify untested code
- Write E2E tests in separate files:
*.e2e.spec.ts