This document provides comprehensive guidance for testing the action-reporting-cli project, including setup instructions, best practices, and architectural guidelines.
The action-reporting-cli project uses Jest as its testing framework and follows a comprehensive testing strategy with:
- Unit Tests: Testing individual modules in isolation
- Integration Tests: Testing interaction between components
- Mocking: Isolating external dependencies (GitHub API, file system)
- Fixtures: Providing realistic test data for consistent testing
- Node.js >= 20
- npm >= 10
Tests are automatically set up when you install the project dependencies:
npm installThe testing configuration is defined in jest.config.js.
# Run all tests
npm test
# Run tests in watch mode (reruns on file changes)
npm run test:watch
# Run specific test file
npm test -- test/github/workflow.test.js
# Run tests matching a pattern, e.g., "throw" in test names
npm test -- --testNamePattern="throw"
# Run tests for specific directory
npm test -- test/github/
# Debug mode (shows console.log output)
npm test -- --verbose --silent=falseTests are run automatically via lint-staged and husky pre-commit hooks, ensuring code quality before commits.
The CI pipeline also runs all tests on every push and pull request to maintain code integrity.
Complete Coverage Achieved: All 16 source modules have corresponding test files.
Fixtures (Test Data):
| File | Description |
|---|---|
test/__fixtures__/common-options.json |
Common CLI options for testing |
test/github/__fixtures__/enterprise-orgs.json |
Sample enterprise organizations data |
test/github/__fixtures__/repositories.json |
Sample repository listings |
test/github/__fixtures__/sample-workflow.yml |
Sample GitHub Actions workflow |
test/github/__fixtures__/workflow-config.json |
Workflow configuration data |
test/github/__fixtures__/workflow-without-permissions.yml |
Workflow without permissions |
test/github/__fixtures__/workflows.json |
Sample workflows data |
test/report/__fixtures__/test-data.json |
Report generation test data |
Mocks (Dependency Substitutes):
| File | Description |
|---|---|
test/__mocks__/fs.js |
File system operations mock |
test/github/__mocks__/octokit.js |
GitHub API client mock |
test/github/__mocks__/repository.js |
Repository operations mock |
test/util/__mocks__/log.js |
Logging utility mock |
The Jest configuration provides clean import paths:
// Instead of relative paths
import workflowsData from '../../../test/github/__fixtures__/workflows.json'// Use mapped paths
import workflowData from 'fixtures/github/workflows.json'
import mockOctokit from '@mocks/github/octokit'Follow the standard Jest structure for all test files:
/**
* Unit tests for [module name].
*/
import {jest} from '@jest/globals'
import Module from '../../src/path/module.js'
// Mock dependencies at the top level
jest.mock('../../src/dependency.js')
describe('[module name]', () => {
let instance
beforeEach(() => {
// Set up test instance and reset mocks
jest.clearAllMocks()
instance = new Module(testOptions)
})
afterEach(() => {
// Clean up after each test
jest.resetAllMocks()
})
describe('methodName', () => {
test('should handle normal case', () => {
// Arrange
const input = 'test-input'
const expected = 'expected-output'
// Act
const result = instance.methodName(input)
// Assert
expect(result).toBe(expected)
})
test('should handle error case', () => {
// Test error scenarios
expect(() => instance.methodName(null)).toThrow('Invalid input')
})
})
})- Test files:
[module-name].test.js - Test descriptions: Use "should" statements that describe expected behavior
- Variables: Use descriptive names that make tests self-documenting
- Group related tests using
describe()blocks - Test both success and failure scenarios
- Include edge cases and boundary conditions
- Test one behavior per test case
Mock all external dependencies to ensure tests are:
- Fast: No network calls or file system operations
- Reliable: Not dependent on external services
- Isolated: Each test runs independently
// Mock GitHub API
jest.mock('../../src/github/octokit.js', () => ({
__esModule: true,
default: jest.fn().mockImplementation(() => ({
paginate: jest.fn(),
request: jest.fn(),
})),
}))
// Mock file system
jest.mock('fs', () => ({
writeFileSync: jest.fn(),
existsSync: jest.fn().mockReturnValue(true),
}))Leverage the module name mapping for cleaner imports:
// Instead of inline mocking, use centralized mocks
jest.mock('../../src/github/octokit.js', () => import('@mocks/github/octokit'))// Use fixtures for test data
import testData from 'fixtures/github/workflows.json'
import commonOptions from 'fixtures/common-options.json'- Store test data in
__fixtures__/directories - Use realistic data that represents actual API responses
- Keep fixtures focused and minimal
- Version control all fixture files
{
"workflow": {
"name": "CI",
"permissions": {
"contents": "read",
"actions": "read"
},
"jobs": {
"test": {
"runs-on": "ubuntu-latest",
"steps": []
}
}
}
}Always test error conditions alongside success cases:
describe('fetchWorkflows', () => {
test('should return workflows on success', async () => {
// Test success case
})
test('should handle API errors gracefully', async () => {
// Mock API error
mockOctokit.request.mockRejectedValue(new Error('API Error'))
await expect(repository.fetchWorkflows()).rejects.toThrow('API Error')
})
test('should handle rate limiting', async () => {
// Test rate limiting scenario
})
})- Keep test execution time under 10ms per test
- Use mocks to avoid slow operations
- Group slow tests separately if needed
- Monitor total test suite runtime
- Aim for 90%+ line coverage
- Focus on testing business logic thoroughly
- Don't chase 100% coverage at the expense of meaningful tests
- Exclude generated files and trivial code from coverage
# Generate detailed coverage report
npm test -- --coverage- Check if the test is correct first - Sometimes tests need updates when functionality changes
- Verify mocks are properly configured - Ensure mocks match actual API responses
- Check for timing issues - Use proper async/await patterns
# Run a specific test file
npm test -- github/workflow.test.js
# Run tests with more verbose output
npm test -- --verbose --silent=false
# Run tests in watch mode for development
npm run test:watch// Log mock calls to debug issues
console.log(mockFunction.mock.calls)
console.log(mockFunction.mock.results)
// Reset mocks between tests
beforeEach(() => {
jest.clearAllMocks()
})When testing the CLI tool itself, you can run it manually for verification:
# Test repository reporting
node cli.js \
--token $(gh auth token) \
--all \
--exclude \
--csv $(pwd)/reports/repository.csv \
--json $(pwd)/reports/repository.json \
--md $(pwd)/reports/repository.md \
--repository stoe/action-reporting-cli \
--debug
# Test user reporting
node cli.js \
--token $(gh auth token) \
--all \
--exclude \
--csv $(pwd)/reports/user.csv \
--json $(pwd)/reports/user.json \
--md $(pwd)/reports/user.md \
--owner stoe \
--debug- Create test file following the naming convention:
[module].test.js - Follow the standard structure with describe/test blocks
- Add fixtures if you need test data
- Create mocks for external dependencies
- Update this documentation if you add new patterns
- Test behavior, not implementation - Focus on what the code should do
- Keep tests simple and focused - One assertion per test when possible
- Use descriptive test names - Make it clear what's being tested
- Test edge cases - Include boundary conditions and error scenarios
- Mock external dependencies - Keep tests fast and reliable
- All new code has corresponding tests
- Tests follow the established patterns
- Mocks are used appropriately
- Test data is in fixtures, not inline
- Tests run quickly (under 10ms each)
- Error cases are tested
- Documentation is updated if needed
This testing guide provides the foundation for maintaining high-quality, reliable tests in the action-reporting-cli project. The comprehensive test suite ensures:
- Reliability: All functionality is verified through automated tests
- Maintainability: Well-organized structure makes tests easy to update
- Developer Experience: Clear patterns and good tooling support efficient development
- Quality Assurance: Comprehensive coverage catches issues early
The testing infrastructure supports confident development and ensures the action-reporting-cli tool remains robust and dependable for users analyzing GitHub Actions workflows.