tests/general/Help.Tests.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
<#
.NOTES The original test this is based upon was written by June Blender. After several rounds of modifications it stands now as it is, but the honor remains hers. Thank you June, for all you have done! .DESCRIPTION This test evaluates the help for all commands in a module. .PARAMETER SkipTest Disables this test. .PARAMETER CommandPath List of paths under which the script files are stored. This test assumes that all functions have their own file that is named after themselves. These paths are used to search for commands that should exist and be tested. Will search recursively and accepts wildcards, make sure only functions are found .PARAMETER ModuleName Name of the module to be tested. The module must already be imported .PARAMETER ExceptionsFile File in which exceptions and adjustments are configured. In it there should be two arrays and a hashtable defined: $global:FunctionHelpTestExceptions $global:HelpTestEnumeratedArrays $global:HelpTestSkipParameterType These can be used to tweak the tests slightly in cases of need. See the example file for explanations on each of these usage and effect. #> [CmdletBinding()] Param ( [switch] $SkipTest, [string[]] $CommandPath = @("$PSScriptRoot\..\..\functions", "$PSScriptRoot\..\..\internal\functions"), [string] $ModuleName = "PSCUCM", [string] $ExceptionsFile = "$PSScriptRoot\Help.Exceptions.ps1" ) if ($SkipTest) { return } . $ExceptionsFile $includedNames = (Get-ChildItem $CommandPath -Recurse -File | Where-Object Name -like "*.ps1").BaseName $commands = Get-Command -Module (Get-Module $ModuleName) -CommandType Cmdlet, Function, Workflow | Where-Object Name -in $includedNames ## When testing help, remember that help is cached at the beginning of each session. ## To test, restart session. foreach ($command in $commands) { $commandName = $command.Name # Skip all functions that are on the exclusions list if ($global:FunctionHelpTestExceptions -contains $commandName) { continue } # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets $Help = Get-Help $commandName -ErrorAction SilentlyContinue $testhelperrors = 0 $testhelpall = 0 Describe "Test help for $commandName" { $testhelpall += 1 if ($Help.Synopsis -like '*`[`<CommonParameters`>`]*') { # If help is not found, synopsis in auto-generated help is the syntax diagram It "should not be auto-generated" { $Help.Synopsis | Should -Not -BeLike '*`[`<CommonParameters`>`]*' } $testhelperrors += 1 } $testhelpall += 1 if ([String]::IsNullOrEmpty($Help.Description.Text)) { # Should be a description for every function It "gets description for $commandName" { $Help.Description | Should -Not -BeNullOrEmpty } $testhelperrors += 1 } $testhelpall += 1 if ([String]::IsNullOrEmpty(($Help.Examples.Example | Select-Object -First 1).Code)) { # Should be at least one example It "gets example code from $commandName" { ($Help.Examples.Example | Select-Object -First 1).Code | Should -Not -BeNullOrEmpty } $testhelperrors += 1 } $testhelpall += 1 if ([String]::IsNullOrEmpty(($Help.Examples.Example.Remarks | Select-Object -First 1).Text)) { # Should be at least one example description It "gets example help from $commandName" { ($Help.Examples.Example.Remarks | Select-Object -First 1).Text | Should -Not -BeNullOrEmpty } $testhelperrors += 1 } if ($testhelperrors -eq 0) { It "Ran silently $testhelpall tests" { $testhelperrors | Should -be 0 } } $testparamsall = 0 $testparamserrors = 0 Context "Test parameter help for $commandName" { $Common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable' $parameters = $command.ParameterSets.Parameters | Sort-Object -Property Name -Unique | Where-Object Name -notin $common $parameterNames = $parameters.Name $HelpParameterNames = $Help.Parameters.Parameter.Name | Sort-Object -Unique foreach ($parameter in $parameters) { $parameterName = $parameter.Name $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ $parameterName $testparamsall += 1 if ([String]::IsNullOrEmpty($parameterHelp.Description.Text)) { # Should be a description for every parameter It "gets help for parameter: $parameterName : in $commandName" { $parameterHelp.Description.Text | Should -Not -BeNullOrEmpty } $testparamserrors += 1 } $testparamsall += 1 $codeMandatory = $parameter.IsMandatory.toString() if ($parameterHelp.Required -ne $codeMandatory) { # Required value in Help should match IsMandatory property of parameter It "help for $parameterName parameter in $commandName has correct Mandatory value" { $parameterHelp.Required | Should -Be $codeMandatory } $testparamserrors += 1 } if ($HelpTestSkipParameterType[$commandName] -contains $parameterName) { continue } $codeType = $parameter.ParameterType.Name $testparamsall += 1 if ($parameter.ParameterType.IsEnum) { # Enumerations often have issues with the typename not being reliably available $names = $parameter.ParameterType::GetNames($parameter.ParameterType) if ($parameterHelp.parameterValueGroup.parameterValue -ne $names) { # Parameter type in Help should match code It "help for $commandName has correct parameter type for $parameterName" { $parameterHelp.parameterValueGroup.parameterValue | Should -be $names } $testparamserrors += 1 } } elseif ($parameter.ParameterType.FullName -in $HelpTestEnumeratedArrays) { # Enumerations often have issues with the typename not being reliably available $names = [Enum]::GetNames($parameter.ParameterType.DeclaredMembers[0].ReturnType) if ($parameterHelp.parameterValueGroup.parameterValue -ne $names) { # Parameter type in Help should match code It "help for $commandName has correct parameter type for $parameterName" { $parameterHelp.parameterValueGroup.parameterValue | Should -be $names } $testparamserrors += 1 } } else { # To avoid calling Trim method on a null object. $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() } if ($helpType -ne $codeType) { # Parameter type in Help should match code It "help for $commandName has correct parameter type for $parameterName" { $helpType | Should -be $codeType } $testparamserrors += 1 } } } foreach ($helpParm in $HelpParameterNames) { $testparamsall += 1 if ($helpParm -notin $parameterNames) { # Shouldn't find extra parameters in help. It "finds help parameter in code: $helpParm" { $helpParm -in $parameterNames | Should -Be $true } $testparamserrors += 1 } } if ($testparamserrors -eq 0) { It "Ran silently $testparamsall tests" { $testparamserrors | Should -be 0 } } } } } |