Testing Guide
Overview
This guide covers the testing strategy for the EyeNet project, including unit tests, integration tests, and end-to-end tests.
Test Structure
tests/
├── unit/ # Unit tests
│ ├── controllers/ # Controller tests
│ ├── services/ # Service tests
│ └── utils/ # Utility tests
├── integration/ # Integration tests
│ ├── api/ # API tests
│ └── db/ # Database tests
└── e2e/ # End-to-end tests
└── flows/ # User flow tests
Running Tests
Unit Tests
# Run all unit tests
npm run test:unit
# Run specific test file
npm run test:unit -- controllers/networkController.test.ts
# Run with coverage
npm run test:unit:coverage
Integration Tests
# Run all integration tests
npm run test:integration
# Run specific integration test
npm run test:integration -- api/auth.test.ts
E2E Tests
# Run all E2E tests
npm run test:e2e
# Run specific E2E test
npm run test:e2e -- flows/login.test.ts
Writing Tests
Unit Test Example
import { NetworkController } from '../controllers/networkController';
import { MockDevice } from './mocks/device';
describe('NetworkController', () => {
let controller: NetworkController;
let mockDevice: MockDevice;
beforeEach(() => {
mockDevice = new MockDevice();
controller = new NetworkController(mockDevice);
});
it('should connect to device successfully', async () => {
const result = await controller.connect();
expect(result.status).toBe('connected');
expect(mockDevice.isConnected()).toBe(true);
});
it('should handle connection failure', async () => {
mockDevice.simulateFailure();
await expect(controller.connect()).rejects.toThrow('Connection failed');
});
});
Integration Test Example
import request from 'supertest';
import { app } from '../app';
import { db } from '../db';
describe('Authentication API', () => {
beforeAll(async () => {
await db.connect();
});
afterAll(async () => {
await db.disconnect();
});
it('should authenticate user with valid credentials', async () => {
const response = await request(app)
.post('/api/auth/login')
.send({
username: 'testuser',
password: 'testpass'
});
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('token');
});
});
E2E Test Example
import { test, expect } from '@playwright/test';
test('user can log in and view dashboard', async ({ page }) => {
await page.goto('/login');
// Fill login form
await page.fill('[name=username]', 'testuser');
await page.fill('[name=password]', 'testpass');
await page.click('button[type=submit]');
// Check dashboard loaded
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toHaveText('Network Dashboard');
});
Test Coverage
Coverage Goals
- Unit Tests: 80% coverage
- Integration Tests: 70% coverage
- Critical Paths: 100% coverage
Generating Coverage Reports
# Generate full coverage report
npm run test:coverage
# View report
open coverage/lcov-report/index.html
Mocking
Network Devices
class MockNetworkDevice implements INetworkDevice {
private connected = false;
async connect(): Promise<void> {
this.connected = true;
}
async disconnect(): Promise<void> {
this.connected = false;
}
isConnected(): boolean {
return this.connected;
}
}
API Responses
const mockApiResponse = {
data: {
devices: [
{ id: '1', name: 'Router1', status: 'online' },
{ id: '2', name: 'Switch1', status: 'offline' }
]
}
};
jest.mock('../api', () => ({
fetchDevices: jest.fn().mockResolvedValue(mockApiResponse)
}));
Test Environment
Configuration
// test/config.ts
export const testConfig = {
database: {
url: 'mongodb://localhost:27017/eyenet_test'
},
api: {
baseUrl: 'http://localhost:3000/api'
}
};
Setup and Teardown
// test/setup.ts
import { setupTestDatabase } from './utils/db';
import { setupTestServer } from './utils/server';
beforeAll(async () => {
await setupTestDatabase();
await setupTestServer();
});
afterAll(async () => {
await cleanupTestDatabase();
await cleanupTestServer();
});
Continuous Integration
GitHub Actions
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test:ci
- name: Upload coverage
uses: codecov/codecov-action@v2
Best Practices
-
Test Organization
- Group related tests
- Use descriptive names
- Follow AAA pattern (Arrange, Act, Assert)
-
Test Independence
- Each test should be independent
- Clean up after tests
- Don't share state
-
Test Data
- Use factories for test data
- Avoid hardcoded values
- Use meaningful test data
-
Assertions
- Make specific assertions
- Test edge cases
- Verify error conditions
-
Maintenance
- Keep tests simple
- Update tests with code changes
- Remove obsolete tests