Pester の使い方｜基礎編

# 1. Pester とは

Pester とは PowerShell にて一番普及しているテスティングフレームワークです。
`Describe`, `Context`, `It` といったキーワードでテストケースを簡潔に記述することができます。

https://pester.dev/

本稿では Pester 5 系を前提に基本的な使い方について解説していきます。
テストコード実装時に役立つの具体的な機能については [Pester の使い方｜実装編](https://qiita.com/nimzo6689/items/c19ad02f7f510e87ea49) に投稿しました。

## 1-1. 主な特徴

<dl>
  <dt>テスト駆動開発ができる</dt>
  <dd>一般的なテスティングフレームワークで導入されている主要機能は提供されているため、<br>
テストコードの実装（Failed） → プログラムを実装 → テストコードの実行（Pass）とリファクタリング、といったサイクルで開発が進められます。</dd>
  <dt>Visual Studio Code 上で利用しやすい</dt>
  <dd>PowerShell チームが提供している <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell">PowerShell Extension for VS Code</a> にて <code>Describe</code> ごとに実行・デバッグができます。</dd>
  <dt>CI/CD で活用できる</dt>
  <dd>GitHub, Jenkins などの CI サーバーとの統合がしやすいです。</dd>
</dl>

# 2. 環境構築

Windows では標準搭載されていますが最新版とはなっていないため、
PSGallery からインストールすることを推奨します。

方法はいくつかありますが、私の場合は一つの PC に複数の Pester が入っているのが嫌なので、
管理者権限で標準搭載されている Pester を削除して再インストールしなおすことが多いです。

以下、公式サイト通りですが、その手順になります。

```powershell:管理者権限で実施
$module = "C:\Program Files\WindowsPowerShell\Modules\Pester"
takeown /F $module /A /R
icacls $module /reset
icacls $module /grant "*S-1-5-32-544:F" /inheritance:d /T
Remove-Item -Path $module -Recurse -Force -Confirm:$false

Install-Module -Name Pester -Force
```

インストールした Pester は以下のコマンドで確認できます。

```powershell
Get-Module -ListAvailable Pester

    Directory: C:\Program Files\WindowsPowerShell\Modules

ModuleType Version   PreRelease Name     PSEdition ExportedCommands
---------- -------   ---------- ----     --------- ----------------
Script     5.5.0                Pester   Desk      {Invoke-Pester, Describe, Context, It…}
```

以降、更新の際は以下のコマンドを実施します。

```powershell:管理者権限で実施
Update-Module -Name Pester
```

他のやり方は [こちら](https://pester.dev/docs/introduction/installation) をご参照ください。

# 3. 簡単なサンプルコードと実行方法

今回は簡単な例として、割り算の商を返すコマンドレットを作成します。

```powershell:Get-Quotient.ps1
function Get-Quotient {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0)]
        [int]$dividend,
        [Parameter(Mandatory=$true, Position=1)]
        [int]$divisor
    )

    if ($divisor -eq 0) {
        throw "割る数に 0 は利用できません。"
    }

    $quotient = $dividend / $divisor
    return [int]$quotient   # 商を整数に変換して返す
}
```

続いて、テストコードを実装していきます。

```powershell:Get-Quotient.Tests.ps1
BeforeAll {
    # テスト対象のファイルパス($PSScriptRoot/Get-Quotient.ps1)を直接書くでもいいですが、
    # このように書くとスペルミスが防止できて便利です。
    . $PSCommandPath.Replace('.Tests.ps1','.ps1')
}

Describe 'Get-Quotient' {
    Context '計算結果の妥当性確認' {
        It '割り切れる数で商が計算できるか' {
            Get-Quotient 12 3 | Should -Be 4
        }
        It '割り切れない数で商が計算できるか' {
            Get-Quotient 12 5 | Should -Be 2
        }
    }

    Context 'コマンドレットの機能確認' {
        It 'パラメータ名を指定して実行できるか' {
            Get-Quotient -divisor 3 -dividend 12 | Should -Be 4
        }
    }
}
```

以下、テストコードを実行した結果です。

```powershell
Invoke-Pester .

#=>Starting discovery in 1 files.
#=>Discovery found 3 tests in 40ms.
#=>Running tests.
#=>[+] <snip>\Pester-Demo\Get-Quotient.Tests.ps1 239ms (17ms|184ms)
#=>Tests completed in 494ms
#=>Tests Passed: 3, Failed: 0, Skipped: 0 NotRun: 0
```

逆にテストケースが失敗したケースを見てみます。
以下のように１ケースだけ修正して、再度実行してみます。

```powershell:Get-Quotient.Tests.ps1
It '割り切れる数で商が計算できるか' {
    Get-Quotient 12 3 | Should -Be 3 # 4 であるべきところを 3 に修正
}
```

```powershell
Invoke-Pester .

#=>Starting discovery in 1 files.
#=>Discovery found 3 tests in 124ms.
#=>Running tests.
#=>[-] Get-Quotient.計算結果の妥当性確認.割り切れる数で商が計算できるか 58ms (56ms|2ms)
#=> at Get-Quotient 12 3 | Should -Be 3, <snip>\Pester-Demo\Get-Quotient.Tests.ps1:10
#=> at <ScriptBlock>, <snip>\Pester-Demo\Get-Quotient.Tests.ps1:10
#=> Expected 3, but got 4.
#=>Tests completed in 489ms
#=>Tests Passed: 2, Failed: 1, Skipped: 0 NotRun: 0
```

上記のように期待値と実際の値がどのテストケースで出力されているのかが確認できます。

# 4. Describe / Context / It の利用用途

Pester における `Describe`, `Context`, `It` は、テストコードを階層的に構造化するためのコマンドレットです。
これにより、テストコードの可読性を向上させ、テストの意図を明確に伝えられるようになります。

それぞれ以下のような使い分けになります。

## 4-1. Describe

`Describe` は、テストの階層をグループ化するために使用され、
一つの `Describe` ブロックには複数の `Context` や `It` ブロックを含めることができます。
ファイルや関数単位で作成されるイメージです。

## 4-2. Context

`Context` は、`Describe` ブロック内のテストケースのさらなるグループ化を行うために使用されます。
`Context` を作成したほうがよいという判断基準などは公式からも明示されていないですが、
GitHub 見る限り、正常系/異常系であったり、ファイルの文字コード/バージョンごとの違い、など
テスト観点ごとのグループで作成するところが多いようです。

## 4-3. It

`It` は、１つのテストケースを記述するために使用されます。
基本的に一つ以上のアサーション(`Should`)を含ますことが一般的と思います。

# 5. ディレクトリ/ファイル名の規約

Pester のテストコードを実装したファイルは基本的に `Tests.ps1` で作成する必要があります。
Pester のデフォルトの設定で、このファイルパターンで検索をかけているためです。

`Tests.ps1` ファイルの置き場は、テスト対象のファイルと同じディレクトリでもいいですし、
ルート配下に `Tests` のようなディレクトリを作成して、テスト対象のファイルが入っているディレクトリと
同じような構成で配置するのでも大丈夫です。

ただ、 GitHub のプロジェクトを見る限り、テスト対象のファイルを同じディレクトリに配置しない派が圧倒的のようです。
（Module 公開するときに `Tests.ps1` ファイルを簡単に除外できるから？）

# 6. テストコードの実行方法

改めてテストコードの実行方法について解説します。

## 6-1. CLI での実行

`Invoke-Pester` コマンドレットで実行します。
主な実行方法は以下です。

```powershell
# `Invoke-Pester .` と同じ意味。
# サブディレクトリも含め、カレントディレクトリ配下の全ての Tests.ps1 ファイルが実行される。
Invoke-Pester

# 特定のファイルだけ実行する。
Invoke-Pester .\Get-Quotient.Tests.ps1

# 特定のファイルの特定の Describe ブロックだけ実行する
Invoke-Pester .\Get-Quotient.Tests.ps1 -Name 'Get-Quotient'

# Get から始まる Describe ブロックだけ実行する
Invoke-Pester -Name 'Get-*'
```

## 6-2. Visual Studio Code での実行

Visual Studio Code の [PowerShell の拡張機能](https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell) を使うことで、
CodeLens から実行やデバッグができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/77833da9-2fca-5eba-b133-a4d72219a9aa.png)

:::note warn
注意
CodeLens を有効にするためには、Tests.ps1 が入っている、
または、上位階層のディレクトリを Workspace として開く必要があります。
:::