Status: active
Priority: P1 (high - quality & confidence)
Complexity: High (multi-phase, ~2-3 sessions)
Implement comprehensive testing infrastructure for oh-my-pwsh with automated tests, code coverage, CI/CD integration, and optional git hooks.
Currently we have:
- ❌ No automated tests (Pester)
- ❌ No code coverage metrics
- ❌ No CI/CD test pipeline
- ❌ No quality gates before merge
- ✅ Only manual test scripts (test-*.ps1)
Risks without testing:
- Breaking changes go unnoticed
- Regressions in production
- Fear of refactoring
- Manual testing burden
- No confidence in deployments
See TESTING-STRATEGY.md for full architectural overview.
This task implements the following ADRs:
- ADR-001 - Pester 5.x as test framework
- ADR-002 - 3-layer test strategy
- ADR-003 - Differentiated coverage targets
- ADR-004 - Optional pre-commit hooks
Testing Infrastructure
├── Layer 1: Test Foundation
│ ├── Pester 5.x installation
│ ├── Test folder structure (Unit/Integration/E2E)
│ └── Test helpers & fixtures
│
├── Layer 2: Test Suite
│ ├── Unit tests (fast, isolated)
│ ├── Integration tests (module interactions)
│ └── E2E tests (full profile scenarios)
│
├── Layer 3: Test Runner
│ ├── Invoke-Tests.ps1 (orchestrator)
│ ├── Coverage reporting
│ └── Multiple output formats
│
├── Layer 4: CI/CD Pipeline
│ ├── GitHub Actions workflows
│ ├── Test matrix (OS, PS versions)
│ └── Coverage upload & badges
│
└── Layer 5: Developer Tools
├── Git hooks (optional)
├── Test scaffolding
└── Watch mode
Goal: Basic testing infrastructure working locally
- Create
scripts/Install-TestDeps.ps1- Install Pester 5.5.0+
- Verify installation
- Check PowerShell version
- Document installation in README
- Create folder structure:
tests/ ├── Unit/ ├── Integration/ ├── E2E/ ├── Fixtures/ ├── Helpers/ └── Coverage/ (gitignored) - Add
.gitignoreentries for test outputs
- Implement
scripts/Invoke-Tests.ps1:param( [ValidateSet('Unit', 'Integration', 'E2E', 'All')] [string]$Type = 'All', [switch]$Coverage, [switch]$Fast, [switch]$Watch )
- Support different test types
- Generate coverage reports (HTML + XML)
- Exit codes for CI
-
tests/Unit/Icons.Tests.ps1- Test Get-FallbackIcon for all roles
- Test Get-IconColor
- Test Test-NerdFontSupport
-
tests/Unit/StatusMessage.Tests.ps1- Test Write-StatusMessage with strings
- Test with message segments
- Test -NoIndent parameter
-
tests/Unit/FallbackBehavior.Tests.ps1CRITICAL- Test behavior when bat NOT installed
- Test behavior when eza NOT installed
- Test behavior when NO tools installed
- Run tests:
./scripts/Invoke-Tests.ps1 -Type Unit
Success Criteria:
- ✅ Pester installed
- ✅ At least 15 passing unit tests (including fallback tests)
- ✅ Tests include missing dependency scenarios
- ✅ Tests run in < 30 seconds
- ✅ Coverage report generated
Acceptance Criteria:
- Tests pass on machine with all tools installed
- Tests pass on machine with NO tools installed
- Profile loads without errors in both scenarios
- All fallback paths are tested
Definition of Done:
- Code written and committed
- All tests passing locally
- Coverage report shows ≥ 60% overall
- No errors when running tests
- Documentation updated (README testing section)
Goal: Achieve 75%+ overall coverage
-
tests/Unit/Logger.Tests.ps1- Write-InstallHint
- Write-ToolStatus
- Write-ModuleStatus
- Write-ProfileStatus
-
tests/Unit/LinuxCompat.Tests.ps1- Test all compatibility aliases
- Test fallback behavior
- Target: 90% coverage for Tier 1, 80% for Tier 2
-
tests/Integration/LoggingFlow.Tests.ps1- Test Write-InstallHint → Write-StatusMessage → Get-FallbackIcon flow
- Test message segment composition end-to-end
-
tests/Integration/ToolDetection.Tests.ps1- Test tool detection with mocked Get-Command
- Test status output for installed/missing tools
-
tests/Helpers/TestHelpers.ps1- Mock-ConsoleOutput
- Assert-OutputContains
- New-TempConfig
-
tests/Fixtures/- config-valid.ps1
- config-invalid.ps1
- config-minimal.ps1
Success Criteria:
- ✅ 50+ total tests
- ✅ Coverage ≥ 75% overall
- ✅ Coverage ≥ 90% for Tier 1 components (icons, status-output)
- ✅ All tests pass consistently
Acceptance Criteria:
- All modules have unit tests
- Integration tests cover message segment composition
- Tests cover all missing dependency scenarios
- Install script consistency test implemented
- Builder pattern for test configs working
Definition of Done:
- All unit tests written for Tier 1 & 2 modules
- Integration tests written and passing
- Coverage report shows ≥ 75% overall
- All tests passing on machine with/without tools
- Test helpers and fixtures created
- Code committed with clear commit message
Goal: Automated testing in GitHub Actions
-
.github/workflows/tests.ymlname: Tests on: [push, pull_request] jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: os: [windows-latest, ubuntu-latest] powershell: ['7.4', '7.3'] steps: - uses: actions/checkout@v4 - name: Install dependencies run: ./scripts/Install-TestDeps.ps1 - name: Run tests run: ./scripts/Invoke-Tests.ps1 -Coverage - name: Upload coverage uses: codecov/codecov-action@v3
- Sign up for Codecov/Coveralls
- Add coverage badges to README
- Configure coverage comments on PRs
- Fail CI if tests fail
- Fail CI if coverage < 75%
- Warn if coverage decreases
Success Criteria:
- ✅ CI runs on every PR
- ✅ Tests pass in CI
- ✅ Coverage displayed in GitHub Actions logs
- ✅ Cannot merge if tests fail
Acceptance Criteria:
- GitHub Actions workflow runs on push and PR
- Tests run on Windows and Ubuntu
- Coverage report generated in CI
- Coverage displayed in action logs
- Failing tests block PR merge
- Matrix tests multiple PowerShell versions (7.3, 7.4)
Definition of Done:
-
.github/workflows/tests.ymlcreated - Workflow runs successfully on test PR
- All tests pass in CI
- Coverage visible in action output
- Branch protection rules configured (if applicable)
- Documentation updated with CI badge/status
Goal: Make testing easy and pleasant
- Create
.github/hooks/pre-commit- Run unit tests only (fast)
- Clear error messages
- Bypassable with --no-verify
- Create
scripts/Install-GitHooks.ps1- Copy hooks to .git/hooks/
- Make executable
- Print installation message
- Create
scripts/New-TestFile.ps1param([string]$Path) # Generates test file from template # Creates tests/Unit/<ModuleName>.Tests.ps1
- Test templates in
tests/Helpers/Templates/
- Watch mode:
Invoke-Tests.ps1 -Watch- Re-run tests on file change
- Uses FileSystemWatcher
-
scripts/Show-Coverage.ps1- Opens HTML coverage report in browser
Success Criteria:
- ✅ Git hook installs successfully
- ✅ Hook runs in < 30 seconds
- ✅ Scaffolding generates valid test files
- ✅ Watch mode works
Acceptance Criteria:
-
Install-GitHooks.ps1works without errors - Pre-commit hook runs only unit tests (fast)
- Hook can be bypassed with
--no-verify -
New-TestFile.ps1generates valid Pester tests - Watch mode detects file changes correctly
Definition of Done:
- Git hook script created in
.github/hooks/ - Installation script created and tested
- Hook tested on real commit
- Test scaffolding script created
- Watch mode implemented (optional)
- Documentation updated with hook installation instructions
Goal: Full scenario coverage
-
tests/E2E/ProfileLoad.Tests.ps1- Full profile load without errors
- All modules sourced correctly
- Config loaded
-
tests/E2E/HelpSystem.Tests.ps1- Help command works
- All help topics accessible
-
tests/E2E/ToolIntegration.Tests.ps1- Enhanced tools work when installed
- Fallbacks work when missing
- Profile load time benchmark
- Test execution time tracking
- Regression detection
- Create
docs/TESTING-GUIDE.md- How to write unit tests
- How to write integration tests
- Mocking patterns
- Best practices
Success Criteria:
- ✅ E2E tests cover main scenarios
- ✅ Performance baseline established
- ✅ Developer guide complete
Acceptance Criteria:
- E2E test for full profile load (with all tools)
- E2E test for profile load (with NO tools) CRITICAL
- E2E test for help system
- Performance benchmark for profile load time
-
docs/TESTING-GUIDE.mdcomplete with examples
Definition of Done:
- E2E tests written and passing
- Performance baseline documented
- Testing guide created with:
- How to write unit tests
- How to write integration tests
- Mocking patterns
- Best practices for testing fallbacks
- All documentation reviewed and complete
oh-my-pwsh/
├── tests/
│ ├── Unit/
│ │ ├── Icons.Tests.ps1
│ │ ├── StatusMessage.Tests.ps1
│ │ ├── Logger.Tests.ps1
│ │ ├── LinuxCompat.Tests.ps1
│ │ └── ...
│ ├── Integration/
│ │ ├── LoggingFlow.Tests.ps1
│ │ ├── ToolDetection.Tests.ps1
│ │ └── ...
│ ├── E2E/
│ │ ├── ProfileLoad.Tests.ps1
│ │ ├── HelpSystem.Tests.ps1
│ │ └── ...
│ ├── Fixtures/
│ │ ├── config-valid.ps1
│ │ └── ...
│ ├── Helpers/
│ │ ├── TestHelpers.ps1
│ │ ├── Mocks.ps1
│ │ └── Templates/
│ └── Coverage/ (gitignored)
│
├── scripts/
│ ├── Invoke-Tests.ps1
│ ├── Install-TestDeps.ps1
│ ├── Install-GitHooks.ps1
│ ├── New-TestFile.ps1
│ └── Show-Coverage.ps1
│
├── .github/
│ ├── workflows/
│ │ ├── tests.yml
│ │ └── coverage.yml
│ └── hooks/
│ └── pre-commit
│
└── docs/
├── TESTING-STRATEGY.md
└── TESTING-GUIDE.md
- PowerShell 7.0+
- Git (for hooks)
- Internet connection (for Pester install, CI)
- Pester 5.5.0+ (from PowerShell Gallery)
- Codecov/Coveralls account (for coverage)
- All existing modules must remain testable
- No breaking changes to public APIs
Meta-testing strategy:
- Tests are fast (unit < 100ms)
- Tests are deterministic (no flaky tests)
- Tests are isolated (no shared state)
- Tests have clear names (describes what they test)
- Tests fail when they should
- Mocks are minimal and targeted
Consider using mutation testing to verify test quality:
- Change code intentionally
- Tests should fail
- If tests pass, they're not testing the right thing
- Architecture designed (this task)
- Pester installed
- 20+ unit tests written
- Coverage ≥ 75%
- CI pipeline running
- Documentation complete
- 50+ total tests (unit + integration + E2E)
- Coverage ≥ 85%
- All tier targets met
- Git hooks functional
- Developer guide published
- Coverage badge in README
- Matrix testing (multiple OS/PS versions)
- Performance benchmarks
- Watch mode
- Test scaffolding tools
- ADR-001: Pester Test Framework
- ADR-002: Test Isolation Strategy
- ADR-003: Coverage Targets
- ADR-004: Git Hooks Optional
- TESTING-STRATEGY.md - Overall strategy (to be created)
- TESTING-GUIDE.md - Developer guide (to be created)
- README.md - Will add testing section
- 001-logging-system.md - Code to be tested
- 002-icon-fallback-system.md - Code to be tested
- 004-write-status-message.md - Code to be tested
Estimated Total: 11-15 hours across 3-4 sessions
| Phase | Time | Priority | Dependencies |
|---|---|---|---|
| Phase 1: Foundation | 2-3h | P0 | None |
| Phase 2: Coverage | 3-4h | P0 | Phase 1 |
| Phase 3: CI/CD | 2-3h | P1 | Phase 2 |
| Phase 4: Dev Tools | 2h | P2 | Phase 1 |
| Phase 5: E2E | 2-3h | P3 | Phase 2 |
Recommended Order:
- Phase 1 (session 1)
- Phase 2 (session 2)
- Phase 3 + Phase 4 (session 3)
- Phase 5 (optional, session 4)
| Phase | Depends On | Blocks | Can Run In Parallel With |
|---|---|---|---|
| Phase 1 | None | Phases 2-5 | None (prerequisite for all) |
| Phase 2 | Phase 1 complete | Phase 3, 5 | Phase 4 (partial) |
| Phase 3 | Phase 2 complete | None | Phase 4 |
| Phase 4 | Phase 1 complete | None | Phases 2, 3 |
| Phase 5 | Phase 2 complete | None | Phases 3, 4 |
| Dependency | Required For | Mitigation if Unavailable |
|---|---|---|
| Pester 5.5.0+ | All phases | BLOCKER - must install from PSGallery |
| PowerShell 7.0+ | All phases | BLOCKER - upgrade required |
| Git | Phase 4 only | Skip git hooks, continue with other phases |
| GitHub Actions access | Phase 3 only | Run tests locally only |
| Internet connection | Pester install, CI | Use cached Pester if available |
| Component | Must Work With | Integration Points |
|---|---|---|
| Tests | All modules in modules/ |
Source all modules to test |
| Install script | Profile dependencies | Must match profile's optional tools list |
| Fallback tests | Get-Command mocking |
Mock tool availability |
| Coverage reports | Pester CodeCoverage | Requires Pester 5.x+ |
| Git hooks | Test runner script | Calls Invoke-Tests.ps1 -Type Unit -Fast |
These requirements MUST be met for testing infrastructure to be considered complete:
-
Test Missing Dependencies (CRITICAL)
- Test profile behavior when bat NOT installed
- Test profile behavior when eza NOT installed
- Test profile behavior when NO enhanced tools installed
- Test profile behavior when oh-my-stats NOT installed
- Verify fallback to native PowerShell commands works
-
Install Script Consistency
- Verify install script lists all optional dependencies
- Verify profile has fallback for each tool in install script
- Keep install script and profile requirements synchronized
-
No Regressions
- Tests catch when fallback code is accidentally removed
- Tests catch when conditional handling is broken
- Tests verify profile loads without errors in all scenarios
-
Auto-run Before Push
- Git pre-commit hook runs unit tests
- Hook is fast (< 30 seconds)
- Hook is bypassable for WIP commits
- CI remains ultimate quality gate
Impact: Developers avoid running tests Mitigation:
- Keep unit tests fast (< 100ms each)
- Use parallel execution
- Provide -Fast mode
- Git hooks run unit tests only
Impact: Loss of trust in test suite Mitigation:
- Avoid time-based tests
- Mock external dependencies
- Use deterministic test data
- Retry logic in CI
Impact: Developers commit untested code Mitigation:
- Make hooks optional but recommended
- Document benefits clearly
- Keep hooks fast (< 30s)
- CI remains ultimate gate
Impact: High coverage but poor tests Mitigation:
- Code review for test quality
- Focus on meaningful assertions
- Mutation testing (future)
- Monitor test-to-code ratio
- Codebase is growing
- More contributors expected
- Refactoring planned
- Need confidence in changes
This task addresses:
- No automated testing
- Manual testing burden
- Fear of breaking changes
- No quality gates
- Visual regression testing (screenshots)
- Contract testing (module interfaces)
- Property-based testing (Hypothesis-style)
- Benchmark regression tests