Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

58 Commits

Repository files navigation

Invoke-Pester

This GitHub Action runs Pester tests in PowerShell, producing code coverage and test result artifacts. It automates many tasks to streamline continuous integration for PowerShell projects:

  • Installation and import of required modules.
  • Automatic merging of default configuration, test suite configuration, and direct inputs into a final Pester configuration.
  • Uploading of test results and coverage reports.
  • Clear step summary in GitHub's job logs.

Dependencies

Configuration Hierarchy

The action's behavior is controlled by a layered configuration system, which merges settings from multiple sources in a specific order. The highest-priority settings override lower-priority ones. The order of precedence is as follows:

SettingDefaultTest SuiteDirect InputsResult
Setting_AXX
Setting_BXYY
Setting_CXYZZ

This last-write-wins strategy means you can set global defaults while retaining the flexibility to override them at the action level.

1. Default Configuration

The action defaults use the PesterConfiguration defaults.

Default Configuration
@{
TestDrive=@{
Enabled=$true
}
TestResult=@{
TestSuiteName='Pester'OutputFormat='NUnitXml'OutputEncoding='UTF8'OutputPath='testResults.xml'Enabled=$false
}
Run=@{
ExcludePath=$nullExit=$falseSkipRun=$falsePath='.'Throw=$falsePassThru=$falseSkipRemainingOnFailure='None'ScriptBlock=$nullContainer=$nullTestExtension='.Tests.ps1'
}
Output=@{
CILogLevel='Error'StackTraceVerbosity='Filtered'RenderMode='Auto'CIFormat='Auto'Verbosity='Normal'
}
Debug=@{
ShowNavigationMarkers=$falseShowFullErrors=$falseWriteDebugMessagesFrom=@(
'Discovery''Skip''Mock''CodeCoverage'
)
WriteDebugMessages=$falseReturnRawResultObject=$false
}
TestRegistry=@{
Enabled=$true
}
CodeCoverage=@{
Path=$nullOutputEncoding='UTF8'CoveragePercentTarget='75'UseBreakpoints=$trueExcludeTests=$trueRecursePaths=$trueOutputPath='coverage.xml'SingleHitBreakpoints=$trueEnabled=$falseOutputFormat='JaCoCo'
}
Should=@{
ErrorAction='Stop'
}
Filter=@{
Line=$nullTag=$nullExcludeLine=$nullFullName=$nullExcludeTag=$null
}
}

2. Test Suite Configuration

If your test suite contains a Pester config file (e.g., MyTests.Configuration.psd1 or Pester.Configuration.ps1), the action loads and merges those settings on top of the defaults.

3. Direct Action Inputs

Finally, any inputs specified under the with: clause in your GitHub Action workflow override both the default and test suite config. If you specify CodeCoverage_Enabled: true here, it will enable coverage even if the test suite config says otherwise.

How This Action Processes Your Tests

  1. Prerequisite Setup

    • Installs required PowerShell modules if they're not present.
    • Installs Pester at the latest version by default, or the version matching the Version constraint when set.
    • Imports the exact installed Pester version so the version that runs is deterministic, even when the runner ships a different preinstalled Pester.
    • Imports the modules so the testing framework is ready to use.
  2. Loading Inputs and Configuration

    • Loads a default Pester configuration.
    • If Path points to a location with a Pester configuration file, merges that config.
    • Finally, merges any direct inputs provided in your workflow.
    • The result is a final Pester configuration that determines what tests to run and how to run them.
  3. Add Containers

    • If no containers are explicitly defined, it attempts to discover them automatically (files matching *.Container.*).
    • Adds these containers to the final configuration.
  4. Running the Tests

    • Calls Invoke-Pester using that final configuration.
    • Finds test files/containers.
    • Runs tests, logging pass/fail/skipped/inconclusive.
    • Aggregates outcomes into a final test object.
  5. Generating Reports

    • Test Results (e.g., NUnit/XML) if TestResult_Enabled is true. The file is saved to TestResult_OutputPath.
    • Code Coverage if CodeCoverage_Enabled is true. Saves coverage data (Cobertura, JaCoCo, etc.) to CodeCoverage_OutputPath.
    • These reports are automatically uploaded as workflow artifacts. The artifact names are:
      • <TestSuiteName>-TestResults
      • <TestSuiteName>-CodeCoverage

    [!TIP] Use the TestResult_TestSuiteName input to change the variable name of the artifact.

  6. Summary in GitHub

    • A step summary is generated, showing how many tests passed/failed/skipped along with coverage information.
    • If containers are in use, each container's results appear in a collapsible section.
  7. Publishing Outputs

    • The action sets several outputs for internal usage:
      • TestSuiteName: Name assigned to the test suite.
      • TestResultEnabled: Indicates if test-result output is enabled.
      • TestResultOutputPath: Path to the test result report.
      • CodeCoverageEnabled: Indicates if code coverage is enabled.
      • CodeCoverageOutputPath: Path to the code coverage report.

How to Determine a Test's Outcome

After running your tests, you can assess the overall result by checking the following outputs provided by the action:

  • Outcome: Indicates the GitHub Action step outcome (success or failure).
  • Conclusion: Provides an overall summary (success or failure) of the test run.
  • Executed: Indicates whether tests were executed (True or False).
  • Result: Overall result of the Pester test run (Passed or Failed).
  • PassedCount: Number of passed tests.
  • FailedCount: Number of failed tests.
  • SkippedCount: Number of skipped tests.
  • InconclusiveCount: Number of inconclusive tests.
  • NotRunCount: Number of tests not run.
  • TotalCount: Total number of tests executed.

These values are accessible in your workflow using the step's outputs, for example:

- name: Statusshell: pwshrun: | Write-Host "Outcome: [${{ steps.action-test.outputs.Outcome }}]" Write-Host "Conclusion: [${{ steps.action-test.outputs.Conclusion }}]" Write-Host "Executed: [${{ steps.action-test.outputs.Executed }}]" Write-Host "Result: [${{ steps.action-test.outputs.Result }}]" Write-Host "Passed tests: [${{ steps.action-test.outputs.PassedCount }}]" Write-Host "Failed tests: [${{ steps.action-test.outputs.FailedCount }}]" Write-Host "Skipped tests: [${{ steps.action-test.outputs.SkippedCount }}]" Write-Host "Total tests: [${{ steps.action-test.outputs.TotalCount }}]"

Controlling Workflow Execution Based on Test Outcome/Conclusion

You can use the test outcome and conclusion to control the flow of your GitHub workflow. For example:

  • Using a Shell Step: You might include a step that checks the outcome and exits with a non-zero code if the tests did not pass:

    - name: Status Checkshell: pwshrun: | $outcome = '${{ steps.action-test.outcome }}' Write-Host "Outcome: [$outcome]" if ($outcome -ne 'success') { Write-Error "Tests did not pass. Aborting workflow." exit 1 }
  • Conditional Steps in Workflow YAML: You can conditionally run steps based on the outcome and conclusion:

    - name: Deployif: ${{ steps.action-test.outcome == 'success' && steps.action-test.conclusion == 'success' }}run: | # Deployment commands here

This approach provides full control over the execution flow of your workflow, ensuring that subsequent actions (like deployment) only run if the tests meet your success criteria.

Usage

Below is a typical usage example. (Subsequent sections list all available inputs and outputs.)

name: Pester Testson:
push:
jobs:
test-pester:
runs-on: ubuntu-lateststeps:
- name: Check outuses: actions/checkout@v4
- name: Run Pester Testsuses: PSModule/Invoke-Pester@v3id: action-testcontinue-on-error: truewith:
TestResult_TestSuiteName: IntegrationTestsPath: ./testsRun_Path: ./src
- name: Statusshell: pwshenv:
OUTCOME: ${{ steps.action-test.outcome }}CONCLUSION: ${{ steps.action-test.conclusion }}run: | Write-Host "Outcome: [$env:OUTCOME]" Write-Host "Conclusion: [$env:CONCLUSION]"

Inputs

All inputs are optional unless noted otherwise. For more details, refer to the Pester Configuration documentation.Run.PassThru is forced to $true to ensure the action can capture test results.

InputDescriptionDefault
PathPath to where tests are located or a configuration file.(none)
VersionVersion of Pester to install (NuGet range, e.g. [6.0.0,7.0.0) for any 6.x). Empty installs the latest version.(none)
PrereleaseAllow installing prerelease versions of Pester.false
GuidOptional module identity (GUID) the installed Pester must match; fails fast if a different module named Pester is loaded. Mirrors the GUID key of a #Requires -Modules pin.(none)
ReportAsJsonOutput generated reports in JSON format in addition to the configured format through Pester.true
PrescriptScript to be executed before the test run. This script is executed in the same context as the test run.(none)
Notice_ModeControls when to show notices for test completion.
  • Full - show on success and failure
  • Failed - show only on failure
  • None - disable notices
Failed
StepSummary_ModeControls which tests to show in the GitHub step summary.
  • Full - all tests
  • Failed - only failed tests
  • None - disable summary
Failed
StepSummary_ShowTestOverviewControls whether to show the test overview table in the GitHub step summary.false
StepSummary_ShowConfigurationControls whether to show the configuration details in the GitHub step summary.false
Run_PathDirectories/files to be searched for tests.(none)
Run_ExcludePathDirectories/files to exclude from the run.(none)
Run_ScriptBlockScriptBlocks containing tests to be executed.(none)
Run_ContainerContainerInfo objects containing tests to be executed.(none)
Run_TestExtensionFilter used to identify test files (e.g. .Tests.ps1).(none)
Run_ExitWhether to exit with a non-zero exit code on failure.(none)
Run_ThrowWhether to throw an exception on test failure.(none)
Run_SkipRunDiscovery only, skip actual test run.(none)
Run_SkipRemainingOnFailureSkips remaining tests after the first failure. Options: None, Run, Container, Block.(none)
Filter_TagTags of Describe/Context/It blocks to run.(none)
Filter_ExcludeTagTags of Describe/Context/It blocks to exclude.(none)
Filter_LineFilter by file + scriptblock start line (e.g. C:\tests\file1.Tests.ps1:37).(none)
Filter_ExcludeLineExclude by file + scriptblock start line. Precedence over Filter_Line.(none)
Filter_FullNameFull name of a test with wildcards, joined by dot. E.g. *.describe Get-Item.test1(none)
CodeCoverage_EnabledEnable code coverage.(none)
CodeCoverage_OutputFormatFormat for the coverage report. Possible values: JaCoCo, CoverageGutters, Cobertura.(none)
CodeCoverage_OutputPathWhere to save the code coverage report (relative to the current dir).(none)
CodeCoverage_OutputEncodingEncoding of the coverage file.(none)
CodeCoverage_PathFiles/directories to measure coverage on (by default, reuses Path from the general settings).(none)
CodeCoverage_ExcludeTestsExclude tests themselves from coverage.(none)
CodeCoverage_RecursePathsRecurse through coverage directories.(none)
CodeCoverage_CoveragePercentTargetDesired minimum coverage percentage.(none)
CodeCoverage_UseBreakpointsExperimental: When false, use a Profiler-based tracer instead of breakpoints.(none)
CodeCoverage_SingleHitBreakpointsRemove breakpoints after first hit.(none)
TestResult_EnabledEnable test-result output (e.g. NUnitXml, JUnitXml).(none)
TestResult_OutputFormatPossible values: NUnitXml, NUnit2.5, NUnit3, JUnitXml.(none)
TestResult_OutputPathWhere to save the test-result report (relative path).(none)
TestResult_OutputEncodingEncoding of the test-result file.(none)
TestResult_TestSuiteNameName used for the root test-suite element in the result file.(none)
Should_ErrorActionControls if Should throws on error. Use Stop to throw, or Continue to fail at the end.(none)
Debug_ShowFullErrorsShow Pester internal stack on errors. (Deprecated – overrides Output.StackTraceVerbosity to Full).(none)
Debug_WriteDebugMessagesWrite debug messages to screen.(none)
Debug_WriteDebugMessagesFromFilter debug messages by source. Wildcards allowed.(none)
Debug_ShowNavigationMarkersWrite paths after every block/test for easy navigation in Visual Studio Code.(none)
Debug_ReturnRawResultObjectReturns an unfiltered result object, for development only.(none)
Output_VerbosityVerbosity: None, Normal, Detailed, Diagnostic.(none)
Output_StackTraceVerbosityStacktrace detail: None, FirstLine, Filtered, Full.(none)
Output_CIFormatCI format of error output: None, Auto, AzureDevops, GithubActions.(none)
Output_CILogLevelCI log level: Error or Warning.(none)
Output_RenderModeHow to render console output: Auto, Ansi, ConsoleColor, Plaintext.(none)
TestDrive_EnabledEnable TestDrive.(none)
TestRegistry_EnabledEnable TestRegistry.(none)
DebugEnable debug output.false
VerboseEnable verbose output.false
GitHubVersionVersion of the GitHub module to install during init bootstrap (NuGet range, e.g. [1.8.0,2.0.0)). Empty installs latest.(none)
GitHubPrereleaseAllow installing prerelease versions of the GitHub module.false
WorkingDirectoryThe working directory where the script runs..

Outputs

The action provides the following outputs:

OutputDescription
OutcomeThe outcome of the test run (success/failure)
ConclusionThe conclusion of the test run (success/failure)
ExecutedWhether tests were executed (True/False)
ResultOverall result of Pester test run (Passed/Failed)
FailedCountNumber of failed tests
FailedBlocksCountNumber of failed blocks
FailedContainersCountNumber of failed containers
PassedCountNumber of passed tests
SkippedCountNumber of skipped tests
InconclusiveCountNumber of inconclusive tests
NotRunCountNumber of tests not run
TotalCountTotal count of tests

Examples

Basic Usage

jobs:
test:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4
- name: Run Pester testsuses: PSModule/Invoke-Pester@v1with:
Path: './tests'

Using Test Results in Subsequent Steps

jobs:
test:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4
- name: Run Pester testsuses: PSModule/Invoke-Pester@v3id: action-testwith:
Path: './tests'TestResult_Enabled: 'true'TestResult_OutputPath: './test-results.xml'
- name: Process test resultsif: always()run: | Write-Output "Total tests: ${{ steps.action-test.outputs.TotalCount }}" Write-Output "Passed tests: ${{ steps.action-test.outputs.PassedCount }}" Write-Output "Failed tests: ${{ steps.action-test.outputs.FailedCount }}" Write-Output "Failed blocks: ${{ steps.action-test.outputs.FailedBlocksCount }}" Write-Output "Failed containers: ${{ steps.action-test.outputs.FailedContainersCount }}" Write-Output "Test outcome: ${{ steps.action-test.outputs.Result }}"shell: pwsh
- name: Take action based on test outcomeif: steps.action-test.outputs.Result == 'Passed'run: echo "All tests passed! Ready to proceed with deployment."

With Code Coverage

jobs:
test:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4
- name: Run Pester tests with code coverageid: pesteruses: PSModule/Invoke-Pester@v1with:
Path: './tests'CodeCoverage_Enabled: 'true'CodeCoverage_Path: './src'CodeCoverage_OutputPath: './coverage.xml'CodeCoverage_OutputFormat: 'JaCoCo'

Import a module before pester runs

jobs:
test:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4
- name: Run Pester tests with prescriptid: pesteruses: PSModule/Invoke-Pester@v1with:
Path: './tests'CodeCoverage_Enabled: 'true'CodeCoverage_Path: './src'CodeCoverage_OutputPath: './coverage.xml'CodeCoverage_OutputFormat: 'JaCoCo'Prescript: | Import-Module MyModule

See Also

About

A Pester-infused GitHub Action

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

2 watching

Forks

Releases

Used by

Contributors

Languages