なか日記

一度きりの人生、楽しく生きよう。

PowershellでPesterを使ってテストしてみた

Powershellを久しぶりに使う機会があり、テストしながらスクリプト書きたいな~と調べてたら、Pesterというのがお手軽でわかりやすかったので使ってみることにしました。

昔、psexpectを少し使ってみましたが、Pesterの方が個人的にはわかりやすくて好きだなぁ。 PSExpectでTDD(FizzBuzzでいこう) - なか日記

準備

使うにあたって、簡単な準備が必要です。

ダウンロード

GitHubのページGitHub - pester/Pester: Pester is the ubiquitous test and mock framework for PowerShell.から一式をcloneもしくはダウンロードします。

適当な場所へ配置

zip形式でダウンロードした場合には適当な場所に展開しておきます。

モジュールのインポート

Powershellのコンソールを起動して、Pesterのモジュールをロードしておきます。

> Import-Module Pester\Pester.psm1

使ってみる

それではさっそく使ってみましょう。

ソースの生成

Pesterには製品コードとテストコードの雛形を自動生成してくれる機能があります。コンソールで以下のコマンドレットを実行します。

> New-Fixture .\scripts Sample
Creating => scripts\Sample.ps1
Creating => scripts\Sample.Tests.ps1

すると、scriptsディレクトリ配下に製品コード(Sample.ps1)とテストコード(Sample.Tests.ps1)が生成されます。

それぞれの中身は以下の様になっています。

scripts\Sample.ps1

function Sample {

}

scripts\Sample.Test.ps1

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
. "$here\$sut"

Describe "Sample" {

    It "does something useful" {
        $true | Should Be $false
    }
}

実行結果をパイプラインで繋いで、期待値と比較させています。

テストの実行

とりあえず、テストを実行してみましょう。テストを実行するには Invoke-Pester コマンドレットを使用します。

> Invoke-Pester
Executing all tests in D:\data\Poweshell\PesterSample
Describing Sample
[-]   does something useful 3ms
   Expected: {False}, But was {True}
   at line: 8 in D:\data\Poweshell\PesterSample\scripts\Sample.Tests.ps1
Tests completed in 3ms
Passed: 0 Failed: 1

アサーションが $true | Should Be $false となっているのでテストは失敗します。

ちなみに、テストの実行は以下の様にテストファイルを指定したり、テストファイル内のテストを指定して実行したりもできます。

> Invoke-Pester .\scripts\Sample.Tests.ps1
> Invoke-Pester .\scripts\Sample.Tests.ps1 Sample

テストの書き方

詳しくは「Home · pester/Pester Wiki · GitHub」に載っていますが、簡単に紹介しておきます。

サンプルその1

Describe "Sample" {

    Context "○○の場合" {
        It "テストケース1" {
            $true | Should Be $true
        }

        It "テストケース2" {
            $true | Should Be $true
        }

        Context "○○の場合" {
            It "テストケース3" {
                $true | Should Be $true
            }
        }
    }
    Context "××の場合" {
        It "テストケース4" {
            $true | Should Be $true
        }
    }
}
Describeテストの宣言。MSTestでいうと、TestClassというところかな。この単位で$TestDriveのディレクトリが初期化されます
Contextテストケースをグルーピングするのに使用します。入れ子にもできるみたい。
Itテストケース。MSTestでいうと、TestMethodかな。

アサーション

よく使いそうな物をピックアップします*1。コードは全てグリーンになるパターンで書いてます。

Be

オブジェクトが等しいかどうかを評価します。

    It "Be" {
        "hoge" | Should Be "hoge"
    }

Exist

ファイル(オブジェクトでも文字列でも)が存在するかどうかを評価します。

    It "Exist" {
        "C:\Windows" | Should Exist
        Get-Item "C:\Windows" | Should Exist
    }

Contain

指定されたファイルの中に特定の文字列を含んでいるかどうかを評価します。

    It "Contain" {
       "hoge.txt" | Should Contain "hoge contents"
    }

BeNullOrEmpty

オブジェクトがnullまたは空文字列かどうかを評価します。

    It "BeNullOrEmpty" {
        $null | Should BeNullOrEmpty
        "" | Should BeNullOrEmpty
    }

Match

文字列が正規表現にマッチするかどうかを評価します。

    It "Match" {
        "hoge" | Should Match "[Hh]oge"
    }

Throw

スクリプトブロックが例外を出力するかどうかを評価します。評価するのは例外のオブジェクトではなく、メッセージ(文字列)になります。

    It "Throw" {
        {throw "error message"} | Should Throw "error message"
    }

その他

Setup

MSTestでいうTestInitializeとは全然違うので注意が必要です。 $TestDrive(%Temp%\pester) のフォルダにファイルやフォルダを作成するコマンドになります。 $TestDrive は Describe の単位で初期化されますので、テストケースで行った操作が他のテストケースに影響する場合があります。

Describe "Setup" {
    Setup -File "hoge.txt" "content"
    Setup -Dir  "fuga"

    It "Contain" {
        "$TestDrive\hoge.txt" | Should Contain "content"
        "$TestDrive\fuga" | Should Exist
    }

    It "Delete" {
        Remove-Item "$TestDrive\hoge.txt"
        "$TestDrive\hoge.txt" | Should Not Exist
    }

    It "Exist" {
        "$TestDrive\hoge.txt" | Should Exist  #テストに失敗します
    }
}

最後に

その他にもMockが使えたり、機能は多そうですが今のところ上記機能で十分なので今回はこれで終わります。もっと知りたい人はソースを見るとか、bingってみるとか、今年のPowerShell Advent Calendar 2013にエントリPesterのMock機能をもう少し詳しくを上げられた[twitter:@oota_ken]さんに聞くとか*2して頂ければと思います。

*1:間違ってたらツッコんで

*2:他人に丸投げ