Skip to main content
rule official · GitHub

powershell-pester-6 — GitHub Copilot rule: setup and usage

Directs GitHub Copilot to follow Pester v6 testing patterns in PowerShell. Useful when writing test suites in *.Tests.ps1 without outdated v5 habits.

Install into GitHub Copilot · Manually: put the files in place All methods and platforms ↓
.github/instructions/powershell-pester-6.instructions.md
Open on GitHub

What powershell-pester-6 does

This guideline steers Copilot toward Pester v6 practices across Windows PowerShell 5.1 and PowerShell 7.4+. Every piece of executable logic must reside inside Pester blocks like Describe and It. Test construction data goes into BeforeDiscovery, while runtime imports belong in BeforeAll to avoid cross-file state leaks.

Pester v6 drops Assert-MockCalled in favor of Should -Invoke. Calls missing a -ParameterFilter no longer reach the original cmdlet, so Copilot adds fallback mocks to keep runs stable.

Data-driven runs using -ForEach fail immediately on empty arrays or $null unless -AllowNullOrEmptyForEach is explicitly set. Without clean test fixtures, discovery will break the test run.

What it helps with

  • structure unit tests in *.Tests.ps1 files
  • replace removed Assert-MockCalled assertions with Should -Invoke
  • configure data-driven tests using the -ForEach argument
  • isolate discovery-time data using BeforeDiscovery blocks

Example prompts for the agent

  • Write a Pester v6 test suite for the Storage.psm1 module.
  • Mock the Get-Service command and verify its invocation count with Should -Invoke.
  • Create a parameterized test block using -ForEach with hashtable fixtures.

How to install powershell-pester-6

Manually: put the files in place

.github/instructions/powershell-pester-6.instructions.md

The applyTo field sets which files it applies to

Step by step, with commands for each platform: AGENTS.md vs CLAUDE.md: what they are and which one to use

Questions about powershell-pester-6

How do I install powershell-pester-6 in GitHub Copilot?

Add the instruction file to your repository at .github/instructions/powershell-pester-6.instructions.md. Copilot will pick up the guidelines for PowerShell test files automatically.

What does powershell-pester-6 do?

It enforces Pester v6 conventions for PowerShell test files, guiding Copilot away from deprecated v5 syntax and ensuring tests run in isolation.

How does mocking differ in Pester v6 compared to v5?

Commands like Assert-MockCalled are gone and replaced by Should -Invoke. Also, function calls that do not match a -ParameterFilter no longer execute the underlying real command.

Source text

powershell-pester-6.instructions.md · MIT
---
applyTo: '**/*.Tests.ps1'
description: 'PowerShell Pester testing best practices based on Pester v6 conventions'
---

# PowerShell Pester v6 Testing Guidelines

This guide provides PowerShell-specific instructions for creating automated tests with the PowerShell Pester v6 module. Pester v6 runs on Windows PowerShell 5.1 and PowerShell 7.4+. Follow the general PowerShell scripting guidance in [powershell.instructions.md](./powershell.instructions.md).

Pester v6 is largely compatible with v5, but it makes several previously deprecated behaviours fail fast. Keep each test file self-contained and run the suite with the same PowerShell and Pester versions used by CI.

## File Naming and Structure

- **File Convention:** Use `*.Tests.ps1` naming pattern
- **Placement:** Place test files next to tested code or in dedicated test directories
- **Import Pattern:** Use `BeforeAll { . $PSScriptRoot/FunctionName.ps1 }` to import tested functions
- **No Direct Code:** Put ALL code inside Pester blocks (`BeforeAll`, `Describe`, `Context`, `It`, etc.)

## Test Structure Hierarchy

```powershell
BeforeAll { # Import tested functions }
Describe 'FunctionName' {
    Context 'When condition' {
        BeforeAll { # Setup for context }
        It 'Should behavior' { # Individual test }
        AfterAll { # Cleanup for context }
    }
}
```

## Discovery and Run

- Pester v6 discovers and runs one file at a time. Do not rely on discovery-time side effects from another test file (for example, a global variable, current directory, or imported module).
- Use `BeforeDiscovery` for data needed to construct tests and `BeforeAll` to import modules or initialise runtime state for the file.
- Keep every test file independently runnable; place shared setup in a Pester configuration (`Run.BeforeContainer`) or a repository `Pester.BeforeContainer.ps1` when it truly applies to all files.

```powershell
BeforeDiscovery {
    $cases = Get-Content "$PSScriptRoot/cases.json" | ConvertFrom-Json
}

BeforeAll {
    Import-Module "$PSScriptRoot/MyModule.psm1"
}

Describe 'MyModule' {
    It 'handles <Name>' -ForEach $cases { Invoke-Thing $Name | Should -Be 'ok' }
}
```

## Core Keywords

- **`Describe`**: Top-level grouping, typically named after function being tested
- **`Context`**: Sub-grouping within Describe for specific scenarios
- **`It`**: Individual test cases, use descriptive names
- **`Should`**: Classic assertion command (`Should -Be`, `Should -Throw`, etc.); existing v5 syntax remains supported
- **`Should-*`**: New v6 assertion commands (`Should-Be`, `Should-Throw`, etc.); adopt them deliberately, not as a required rename
- **`BeforeAll/AfterAll`**: Setup/teardown once per block
- **`BeforeEach/AfterEach`**: Setup/teardown before/after each test

## Setup and Teardown

- **`BeforeAll`**: Runs once at start of containing block, use for expensive operations
- **`BeforeEach`**: Runs before every `It` in block, use for test-specific setup
- **`AfterEach`**: Runs after every `It`, guaranteed even if test fails
- **`AfterAll`**: Runs once at end of block, use for cleanup
- **Variable Scoping**: `BeforeAll` variables are available to child blocks (read-only); `BeforeEach`, `It`, and `AfterEach` share their block scope
- **One hook of each type**: A block may contain only one `BeforeAll`, `BeforeEach`, `AfterAll`, or `AfterEach`; combine setup when necessary

## Assertions (Should)

- **Basic Comparisons**: `-Be`, `-BeExactly`, `-Not -Be`
- **Collections**: `-Contain`, `-BeIn`, `-HaveCount`
- **Numeric**: `-BeGreaterThan`, `-BeLessThan`, `-BeGreaterOrEqual`
- **Strings**: `-Match`, `-Like`, `-BeNullOrEmpty`
- **Types**: `-BeOfType`, `-BeTrue`, `-BeFalse`
- **Files**: `-Exist`, `-FileContentMatch`
- **Exceptions**: `-Throw`, `-Not -Throw`

## Mocking

- **`Mock CommandName { ScriptBlock }`**: Replace command behavior
- **`-ParameterFilter`**: Mock only when parameters match condition
- **`-Verifiable`**: Mark mock as requiring verification
- **`Should -Invoke`**: Verify mock was called specific number of times
- **`Should -InvokeVerifiable`**: Verify all verifiable mocks were called
- **Scope**: Mocks default to containing block scope

Pester v6 removes `Assert-MockCalled` and `Assert-VerifiableMock`; replace them with the `Should -Invoke` forms. Calls that miss every `-ParameterFilter` no longer fall through to the real command, so add a default mock whenever unmatched calls are valid.

```powershell
Mock Get-Service { @{ Status = 'Running' } } -ParameterFilter { $Name -eq 'TestService' }
Should -Invoke Get-Service -Exactly 1 -ParameterFilter { $Name -eq 'TestService' }
```

## Test Cases (Data-Driven Tests)

Use `-TestCases` or `-ForEach` for parameterized tests. In Pester v6, `$null` or an empty array throws instead of silently skipping; fix the data source or opt in to an empty case with `-AllowNullOrEmptyForEach` on the specific block.

```powershell
It 'Should return <Expected> for <Input>' -TestCases @(
    @{ Input = 'value1'; Expected = 'result1' }
    @{ Input = 'value2'; Expected = 'result2' }
) {
    Get-Function $Input | Should -Be $Expected
}
```

## Data-Driven Tests

- **`-ForEach`**: Available on `Describe`, `Context`, and `It` for generating multiple tests from data
- **`-TestCases`**: Alias for `-ForEach` on `It` blocks (backwards compatibility)
- **Hashtable Data**: Each item defines variables available in test (e.g., `@{ Name = 'value'; Expected = 'result' }`)
- **Array Data**: Uses `$_` variable for current item
- **Templates**: Use `<variablename>` in test names for dynamic expansion

```powershell
# Hashtable approach
It 'Returns <Expected> for <Name>' -ForEach @(
    @{ Name = 'test1'; Expected = 'result1' }
    @{ Name = 'test2'; Expected = 'result2' }
) { Get-Function $Name | Should -Be $Expected }

# Array approach
It 'Contains <_>' -ForEach 'item1', 'item2' { Get-Collection | Should -Contain $_ }
```

When a test name contains `<...>`, Pester v6 evaluates the token as a PowerShell expression. Backtick-escape the opening `<` when the text should remain literal.

## Tags

- **Available on**: `Describe`, `Context`, and `It` blocks
- **Filtering**: Use `-Tag` and `-ExcludeTag` with `Invoke-Pester`, or set `Filter.Tag` and `Filter.ExcludeTag` in `New-PesterConfiguration`
- **Wildcards**: Tags support `-like` wildcards for flexible filtering

```powershell
Describe 'Function' -Tag 'Unit' {
    It 'Should work' -Tag 'Fast', 'Stable' { }
    It 'Should be slow' -Tag 'Slow', 'Integration' { }
}

# Run only fast unit tests
Invoke-Pester -Tag 'Unit' -ExcludeTag 'Slow'
```

## Skip

- **`-Skip`**: Available on `Describe`, `Context`, and `It` to skip tests
- **Conditional**: Use `-Skip:$condition` for dynamic skipping
- **Runtime Skip**: Use `Set-ItResult -Skipped` or `Set-ItResult -Inconclusive` during test execution (setup/teardown still run)
- **Ends the test body**: `Set-ItResult -Skipped`/`-Inconclusive` throws internally to end the `It` block, so code after it does not run; a trailing `return` is unreachable and should not be added

```powershell
It 'Should work on Windows' -Skip:(-not $IsWindows) { }
Context 'Integration tests' -Skip { }
```

## Error Handling

- **Continue on Failure**: Use `Should.ErrorAction = 'Continue'` to collect multiple failures
- **Stop on Critical**: Use `-ErrorAction Stop` for pre-conditions
- **Test Exceptions**: Use `{ Code } | Should -Throw` for exception testing
- **Pending tests**: `Set-ItResult -Pending` was removed; use `-Inconclusive`, `-Skipped`, or `It -Skip`

## Best Practices

- **Descriptive Names**: Use clear test descriptions that explain behavior
- **AAA Pattern**: Arrange (setup), Act (execute), Assert (verify)
- **Isolated Tests**: Each test should be independent
- **Avoid Aliases**: Use full cmdlet names (`Where-Object` not `?`)
- **Single Responsibility**: One assertion per test when possible
- **Test File Organization**: Group related tests in Context blocks. Context blocks can be nested.

## Example Test Pattern

```powershell
BeforeAll {
    . $PSScriptRoot/Get-UserInfo.ps1
}

Describe 'Get-UserInfo' {
    Context 'When user exists' {
        BeforeAll {
            Mock Get-ADUser { @{ Name = 'TestUser'; Enabled = $true } }
        }

        It 'Should return user object' {
            $result = Get-UserInfo -Username 'TestUser'
            $result | Should -Not -BeNullOrEmpty
            $result.Name | Should -Be 'TestUser'
        }

        It 'Should call Get-ADUser once' {
            Get-UserInfo -Username 'TestUser'
            Should -Invoke Get-ADUser -Exactly 1
        }
    }

    Context 'When user does not exist' {
        BeforeAll {
            Mock Get-ADUser { throw "User not found" }
        }

        It 'Should throw exception' {
            { Get-UserInfo -Username 'NonExistent' } | Should -Throw "*not found*"
        }
    }
}
```

## Configuration

Configuration is defined **outside** test files when calling `Invoke-Pester` to control execution behavior.

```powershell
# Create configuration (Pester v6)
$config = New-PesterConfiguration
$config.Run.Path = './Tests'
$config.Output.Verbosity = 'Detailed'
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = 'NUnitXml'
$config.Should.ErrorAction = 'Continue'
Invoke-Pester -Configuration $config
```

**Key Sections**: Run (Path, Exit), Filter (Tag, ExcludeTag), Output (Verbosity), TestResult (Enabled, OutputFormat), CodeCoverage (Enabled, Path), Should (ErrorAction), Debug

© github · MIT · github/awesome-copilot

Similar IDE rules

All IDE rules for Testing and QA →

From the same set861

The whole set →
quarkus-mcp-server-sse This rule guides development of Quarkus MCP servers, from tool methods to project structure. Use it in Java projects with a Quarkus MCP Server extension. ★ 40,418repo stars qa-engineering-best-practices A GitHub Copilot instruction file covering test strategy, the test pyramid, naming, assertions and bug reports. Useful when Copilot writes or reviews tests in your repo. ★ 40,418repo stars python-mcp-server Instructions for GitHub Copilot to build Model Context Protocol servers using Python SDK v2. Keeps generation aligned with the 2026-07-28 protocol spec. ★ 40,418repo stars powershell A GitHub Copilot rule that pushes it toward idiomatic PowerShell scripts following Microsoft's cmdlet guidelines. Useful when your repo has .ps1 and .psm1 files. ★ 40,418repo stars power-platform-mcp-development This rule guides Power Platform custom connector work with MCP for Microsoft Copilot Studio. Use it when shaping schemas, auth flows, scripts, and validation. ★ 40,418repo stars power-platform-connector A GitHub Copilot rule for writing JSON schemas of Power Platform custom connectors: Swagger 2.0, apiProperties and settings. ★ 40,418repo stars power-bi-security-rls-best-practices Power BI Row-Level Security rule for GitHub Copilot. Guides DAX role filtering, embedded analytics security, and Fabric Warehouse policies. ★ 40,418repo stars power-bi-report-design-best-practices This rule guides Power BI chart selection and report layout. Use it when planning a page, navigation, or visual explanations. ★ 40,418repo stars

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor