Add comprehensive testing framework documentation

Co-authored-by: mountaindude <1029262+mountaindude@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-07-25 20:09:44 +00:00
parent 19201d8685
commit 2af483d3db

323
docs/testing-framework.md Normal file
View File

@@ -0,0 +1,323 @@
# Testing Framework Guide for Butler SOS
This document provides a comprehensive guide for Butler SOS developers on how the testing framework is set up, how to write tests, and how to run them effectively.
## Overview
Butler SOS uses **Jest v30.0.5** as its primary testing framework. The project is configured to work with ES modules and provides comprehensive test coverage across all major components.
## Test Framework Configuration
### Jest Configuration
The Jest configuration is defined in `jest.config.mjs` with the following key settings:
- **Test Environment**: `node` - All tests run in a Node.js environment
- **ES Module Support**: Fully configured for ES modules with `type: "module"` in package.json
- **Coverage Collection**: Enabled by default with V8 coverage provider
- **Coverage Directory**: `./coverage/`
- **Clear Mocks**: Automatically clears mocks between tests
### Package.json Scripts
The following npm scripts are available for testing:
```bash
# Run Jest tests only
npm run jest
# Run full test suite (Jest + Snyk + formatting)
npm run test
# Run only unit tests (alias for jest)
npm run test:unit
```
### ES Module Configuration
Butler SOS runs Jest with specific Node.js flags to support ES modules:
```bash
node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js
```
This configuration:
- Enables experimental VM modules support
- Suppresses warnings for experimental features
- Allows Jest to work seamlessly with ES modules
## Test File Organization
### Directory Structure
Tests are organized using the `__tests__` directory pattern throughout the codebase:
```
src/
├── lib/
│ └── __tests__/
│ ├── heartbeat.test.js
│ ├── post-to-mqtt.test.js
│ ├── config-file-schema.test.js
│ └── ...
├── lib/config-schemas/
│ └── __tests__/
│ ├── destinations.test.js
│ ├── credentials.test.js
│ └── ...
├── lib/udp_handlers/user_events/
│ └── __tests__/
│ └── message-event.test.js
└── plugins/
└── __tests__/
├── sensible.test.js
└── support.test.js
```
### Naming Conventions
- Test files use the `.test.js` extension
- Test files are named after the module they test (e.g., `heartbeat.js``heartbeat.test.js`)
- Test files are placed in `__tests__` directories alongside or near the code they test
## Writing Tests
### Basic Test Structure
All test files follow this ES module import pattern:
```javascript
import { jest, describe, test, expect, beforeEach, afterEach } from '@jest/globals';
import { moduleToTest } from '../module-to-test.js';
describe('module-to-test', () => {
test('should do something', () => {
// Test implementation
expect(result).toBe(expected);
});
});
```
### Mocking Dependencies
Butler SOS uses Jest's ES module mocking capabilities. Here are the common patterns:
#### External Dependencies
```javascript
// Mock external modules before importing them
jest.unstable_mockModule('axios', () => ({
default: {
get: jest.fn(),
post: jest.fn(),
},
}));
// Import after mocking
const axios = (await import('axios')).default;
```
#### Internal Modules
```javascript
// Mock internal globals module
jest.unstable_mockModule('../../globals.js', () => ({
default: {
logger: {
error: jest.fn(),
debug: jest.fn(),
verbose: jest.fn(),
},
config: {
get: jest.fn(),
has: jest.fn(),
},
},
}));
const globals = (await import('../../globals.js')).default;
```
### Configuration Testing
Many tests verify configuration schema validation:
```javascript
import Ajv from 'ajv';
import addKeywords from 'ajv-keywords';
import addFormats from 'ajv-formats';
import configFileSchema from '../config-file-schema.js';
describe('config-file-schema', () => {
let ajv;
beforeEach(() => {
ajv = new Ajv({
strict: true,
allErrors: true,
allowUnionTypes: true,
});
addKeywords(ajv, ['transform']);
addFormats(ajv);
});
test('should validate against minimal config', () => {
const minimalConfig = { /* config object */ };
const validate = ajv.compile(configFileSchema);
const valid = validate(minimalConfig);
expect(valid).toBe(true);
});
});
```
## Running Tests
### Running All Tests
```bash
# Run all tests with coverage
npm run test
# Run only Jest tests
npm run jest
npm run test:unit
```
### Running Specific Tests
```bash
# Run tests matching a pattern
npx jest heartbeat
# Run tests in a specific directory
npx jest src/lib/__tests__/
# Run a specific test file
npx jest src/lib/__tests__/heartbeat.test.js
```
### Running Tests with Options
```bash
# Run tests in watch mode
npx jest --watch
# Run tests with verbose output
npx jest --verbose
# Run tests with coverage report
npx jest --coverage
```
## Test Coverage
### Coverage Configuration
Coverage is automatically collected during test runs and includes:
- **Provider**: V8 (native Node.js coverage)
- **Output Directory**: `./coverage/`
- **Report Format**: Multiple formats including HTML, text, and LCOV
### Viewing Coverage Reports
After running tests, coverage reports are available in:
```
coverage/
├── lcov-report/
│ └── index.html # Detailed HTML coverage report
├── lcov.info # Machine-readable coverage data
└── coverage-final.json
```
Open `coverage/lcov-report/index.html` in a browser to view detailed coverage information.
## Jest v30 Specific Considerations
### ES Module Support
Jest v30 has improved ES module support but requires specific configuration:
1. **No `--runInBand` Flag**: Jest v30 has stricter ES module handling when running tests in band mode. Tests run in parallel by default.
2. **Dynamic Imports**: All dynamic imports (`import()`) in test files are strictly validated.
3. **Module Mocking**: Use `jest.unstable_mockModule()` for ES module mocking before importing modules.
### Breaking Changes from v29
- Removed `--runInBand` from npm scripts due to ES module compatibility issues
- Stricter dynamic import validation
- Improved parallel test execution performance
## Test Categories
### Unit Tests
Located throughout the codebase, these test individual modules:
- **Configuration validation** (`config-*.test.js`)
- **Data processing** (`post-to-*.test.js`)
- **Event handling** (`*-event.test.js`)
- **Utility functions** (`heartbeat.test.js`, `service_uptime.test.js`)
### Integration Tests
Tests that verify module interactions:
- **Plugin loading** (`plugins/__tests__/`)
- **Configuration schemas** (`config-schemas/__tests__/`)
- **Event message handling** (`udp_handlers/__tests__/`)
## Best Practices
### Test Organization
1. **Keep tests close to code**: Place tests in `__tests__` directories near the code they test
2. **One test file per module**: Each module should have a corresponding test file
3. **Descriptive test names**: Use clear, descriptive test and describe block names
### Mocking Strategy
1. **Mock external dependencies**: Always mock HTTP clients, databases, and external services
2. **Mock global state**: Mock the globals module for consistent test isolation
3. **Use beforeEach for setup**: Initialize mocks and test data in beforeEach blocks
### Test Structure
1. **Arrange, Act, Assert**: Structure tests with clear setup, execution, and verification phases
2. **Test edge cases**: Include tests for error conditions and boundary cases
3. **Keep tests focused**: Each test should verify one specific behavior
### Performance
1. **Parallel execution**: Jest v30 runs tests in parallel by default for better performance
2. **Selective test runs**: Use Jest's pattern matching to run only relevant tests during development
3. **Coverage optimization**: Focus coverage collection on areas under active development
## Troubleshooting
### Common Issues
1. **Module not found errors**: Ensure all imports use correct file extensions (`.js`)
2. **ES module import errors**: Check that mocks are set up before imports
3. **Timeout issues**: Increase Jest timeout for long-running async tests
### Debug Tips
1. **Use `--verbose` flag**: Get detailed output for failing tests
2. **Add `console.log`**: Temporary debugging in test files
3. **Run single tests**: Isolate failing tests to debug specific issues
## Continuous Integration
Tests run automatically in CI/CD pipelines with:
- Full test suite execution
- Coverage report generation
- Security scanning with Snyk
- Code formatting validation
The test suite must pass before any code can be merged to main branches.