microsoft/hve-core
Publicmirrored fromhttps://github.com/microsoft/hve-coreAvailable
docs/architecture/testing.md
172lines · modecode
| 1 | --- |
| 2 | title: Testing Architecture |
| 3 | description: PowerShell Pester test infrastructure and conventions |
| 4 | author: Microsoft |
| 5 | ms.date: 2026-01-22 |
| 6 | ms.topic: concept |
| 7 | --- |
| 8 | |
| 9 | ## Overview |
| 10 | |
| 11 | HVE Core uses Pester 5.x for PowerShell testing with a mirror directory structure that maps production scripts to their corresponding test files. The test infrastructure supports isolated unit testing through mock utilities and enforces a 70% code coverage threshold. |
| 12 | |
| 13 | ## Directory Structure |
| 14 | |
| 15 | Test files follow a mirror pattern where each script directory has a corresponding `tests/` subdirectory: |
| 16 | |
| 17 | ```text |
| 18 | scripts/ |
| 19 | ├── dev-tools/ |
| 20 | │ └── Generate-PrReference.ps1 |
| 21 | ├── extension/ |
| 22 | │ ├── Package-Extension.ps1 |
| 23 | │ └── Prepare-Extension.ps1 |
| 24 | ├── lib/ |
| 25 | │ └── Get-VerifiedDownload.ps1 |
| 26 | ├── linting/ |
| 27 | │ └── *.ps1 |
| 28 | ├── security/ |
| 29 | │ └── *.ps1 |
| 30 | └── tests/ |
| 31 | ├── dev-tools/ |
| 32 | │ └── Generate-PrReference.Tests.ps1 |
| 33 | ├── extension/ |
| 34 | ├── lib/ |
| 35 | ├── linting/ |
| 36 | ├── security/ |
| 37 | ├── Fixtures/ |
| 38 | ├── Mocks/ |
| 39 | │ └── GitMocks.psm1 |
| 40 | └── pester.config.ps1 |
| 41 | ``` |
| 42 | |
| 43 | Test files use the `.Tests.ps1` suffix convention, enabling automatic discovery by Pester. |
| 44 | |
| 45 | ## Pester Configuration |
| 46 | |
| 47 | The configuration file at [scripts/tests/pester.config.ps1](../../scripts/tests/pester.config.ps1) defines test execution behavior: |
| 48 | |
| 49 | ```powershell |
| 50 | # Key configuration settings |
| 51 | $configuration.Run.TestExtension = '.Tests.ps1' |
| 52 | $configuration.Filter.ExcludeTag = @('Integration', 'Slow') |
| 53 | $configuration.CodeCoverage.CoveragePercentTarget = 70 |
| 54 | ``` |
| 55 | |
| 56 | ### Coverage Configuration |
| 57 | |
| 58 | Code coverage analyzes scripts in production directories while excluding test files: |
| 59 | |
| 60 | | Setting | Value | |
| 61 | |-------------------|---------------------| |
| 62 | | Coverage target | 70% minimum | |
| 63 | | Output format | JaCoCo XML | |
| 64 | | Output path | `logs/coverage.xml` | |
| 65 | | Excluded patterns | `*.Tests.ps1` | |
| 66 | |
| 67 | Coverage directories include `linting/`, `security/`, `dev-tools/`, `lib/`, and `extension/`. |
| 68 | |
| 69 | ### Test Output |
| 70 | |
| 71 | | Output Type | Format | Path | |
| 72 | |-----------------|----------|---------------------------| |
| 73 | | Test results | NUnitXml | `logs/pester-results.xml` | |
| 74 | | Coverage report | JaCoCo | `logs/coverage.xml` | |
| 75 | |
| 76 | ## Test Utilities |
| 77 | |
| 78 | ### LintingHelpers Module |
| 79 | |
| 80 | The [LintingHelpers.psm1](../../scripts/linting/Modules/LintingHelpers.psm1) module provides shared functions for linting scripts and tests: |
| 81 | |
| 82 | | Function | Purpose | |
| 83 | |---------------------------|-----------------------------------------------------------------| |
| 84 | | `Get-ChangedFilesFromGit` | Detects changed files using merge-base with fallback strategies | |
| 85 | | `Get-FilesRecursive` | Recursively finds files while respecting gitignore patterns | |
| 86 | | `Get-GitIgnorePatterns` | Parses `.gitignore` into PowerShell wildcard patterns | |
| 87 | | `Write-GitHubAnnotation` | Writes GitHub Actions annotations for errors and warnings | |
| 88 | | `Set-GitHubOutput` | Sets GitHub Actions output variables | |
| 89 | | `Set-GitHubEnv` | Sets GitHub Actions environment variables | |
| 90 | |
| 91 | ### GitMocks Module |
| 92 | |
| 93 | The [GitMocks.psm1](../../scripts/tests/Mocks/GitMocks.psm1) module provides reusable mock helpers for Git CLI and GitHub Actions testing. |
| 94 | |
| 95 | #### Environment Management |
| 96 | |
| 97 | | Function | Purpose | |
| 98 | |------------------------------------|---------------------------------------------------------| |
| 99 | | `Save-GitHubEnvironment` | Saves current GitHub Actions environment variables | |
| 100 | | `Restore-GitHubEnvironment` | Restores saved environment state | |
| 101 | | `Initialize-MockGitHubEnvironment` | Creates mock GitHub Actions environment with temp files | |
| 102 | | `Clear-MockGitHubEnvironment` | Removes GitHub Actions environment variables | |
| 103 | | `Remove-MockGitHubFiles` | Cleans up temp files from mock initialization | |
| 104 | |
| 105 | #### Git Mocks |
| 106 | |
| 107 | | Function | Purpose | |
| 108 | |-------------------------------|---------------------------------------------------| |
| 109 | | `Initialize-GitMocks` | Sets up standard git command mocks for a module | |
| 110 | | `Set-GitMockChangedFiles` | Updates files returned by git diff mock | |
| 111 | | `Set-GitMockMergeBaseFailure` | Simulates merge-base failure for fallback testing | |
| 112 | |
| 113 | #### Test Data |
| 114 | |
| 115 | | Function | Purpose | |
| 116 | |---------------------------|---------------------------------------------------| |
| 117 | | `New-MockFileList` | Generates mock file paths for testing | |
| 118 | | `Get-MockGitDiffScenario` | Returns predefined scenarios for git diff testing | |
| 119 | |
| 120 | ### Environment Save/Restore Pattern |
| 121 | |
| 122 | Tests that modify environment variables follow this pattern: |
| 123 | |
| 124 | ```powershell |
| 125 | BeforeAll { |
| 126 | Import-Module "$PSScriptRoot/../Mocks/GitMocks.psm1" -Force |
| 127 | } |
| 128 | |
| 129 | BeforeEach { |
| 130 | Save-GitHubEnvironment |
| 131 | $script:MockFiles = Initialize-MockGitHubEnvironment |
| 132 | } |
| 133 | |
| 134 | AfterEach { |
| 135 | Remove-MockGitHubFiles -MockFiles $script:MockFiles |
| 136 | Restore-GitHubEnvironment |
| 137 | } |
| 138 | ``` |
| 139 | |
| 140 | ## Running Tests |
| 141 | |
| 142 | ### npm Scripts |
| 143 | |
| 144 | | Command | Description | |
| 145 | |-------------------|----------------------| |
| 146 | | `npm run test:ps` | Run all Pester tests | |
| 147 | |
| 148 | ### Direct Pester Invocation |
| 149 | |
| 150 | Run tests with default configuration: |
| 151 | |
| 152 | ```powershell |
| 153 | Invoke-Pester -Configuration (& ./scripts/tests/pester.config.ps1) |
| 154 | ``` |
| 155 | |
| 156 | Run tests with code coverage: |
| 157 | |
| 158 | ```powershell |
| 159 | Invoke-Pester -Configuration (& ./scripts/tests/pester.config.ps1 -CodeCoverage) |
| 160 | ``` |
| 161 | |
| 162 | Run tests in CI mode with exit codes and NUnit output: |
| 163 | |
| 164 | ```powershell |
| 165 | Invoke-Pester -Configuration (& ./scripts/tests/pester.config.ps1 -CI -CodeCoverage) |
| 166 | ``` |
| 167 | |
| 168 | Run a specific test file: |
| 169 | |
| 170 | ```powershell |
| 171 | Invoke-Pester -Path ./scripts/tests/linting/Invoke-PSScriptAnalyzer.Tests.ps1 |
| 172 | ``` |
| 173 | |