Beherrschen Sie PowerShell-Tests mit Data-Driven Pester

Ich benutze Pester schon seit langer Zeit ab und zu. Ich war schon immer besessen davon, die Zuverlässigkeit meines PowerShell-Codes sicherzustellen. Nach dem Schreiben das Pester-Buch und indem ich einige der Methoden erwähne, die ich mit Pester v4 verwendet habe, die ich in diesem Blogbeitrag vorstellen werde, habe ich seitdem gelernt, dass Pester v5 meine Arbeit so viel einfacher macht.

Sie sehen, wenn Sie Tests mit Pester erstellen, sind Ihre Optionen wie alles in PowerShell grenzenlos. Sie können Tests auf millionenfache Weise strukturieren, da Sie in den Tests jede gewünschte PowerShell schreiben können. Aber das ist nicht immer eine gute Sache. Sie benötigen eine Methodik oder eine allgemeine Art und Weise, wie Sie sie erstellen. Das ist mein bevorzugter Weg; datengesteuerte Tests.

Advertisement

Was sind datengesteuerte Tests?

In einer typischen Pester-Testsuite erstellen Sie normalerweise eine it Block für jede Behauptung, die Sie ausführen müssen. Sie erstellen eine it blockieren, um:

  • Bestätigen Sie, dass das Skript die richtige Ausgabe zurückgibt
  • Bestätigen Sie, dass das Skript eine korrekte Funktion aufruft
  • Bestätigen Sie, dass das Skript eine Datei am richtigen Speicherort erstellt.
  • …Die Möglichkeiten sind endlos

Für jedes dieser Szenarios erstellen Sie ein it Block mit einem Namen und geben Sie alle Parameter an, die an das Skript übergeben werden sollen, um die von Ihnen getestete Aktion auszuführen.

Zum Beispiel:

describe 'some script' {	it 'creates a thing given the parameters to do so' {	./thing.ps1 -Create -Name 'xxx' | should -BeTrue	}	it 'sets a thing given the parameters to do so' {	./thing.ps1 -Set -Name 'xxx' | should -BeTrue	}	it 'removes a thing given the parameters to do so' {	./thing.ps1 -Remove -Name 'xxx' | should -BeTrue	}
}

Fair genug. Sie haben jedes Szenario definiert, die erforderlichen Parameter bereitgestellt und die Skriptrückgaben aktiviert true. Dies ist eine gängige Methode zum Erstellen von Tests, aber dieser Ansatz hat mich immer gestört. Es fühlte sich an, als würde ich Code unnötig duplizieren, wenn ich Dinge in einem Array speichern konnte.

Advertisement

Es stellte sich heraus, dass ich es könnte!

In Pester v5 können Sie Tests erstellen, die im Wesentlichen Teil einer foreach-Schleife sind, indem Parametersätze alle auf einmal an Blöcke übergeben werden und Pester den Test für jeden Parametersatz ausführen lässt.

Im obigen Beispiel verwenden Sie drei verschiedene Parametersätze für dasselbe Skript. Diese Sätze können in einem Array von Hashtabellen definiert werden.

$paramSets = @(	@{	Create = $true	Name = 'xxx'	}	{	Set = $true	Name = 'xxx'	}	{	Remove = $true	Name = 'xxx'	}
)

Sobald sie alle in diesem Array gespeichert sind, können Sie Pester v5 verwenden foreach Funktion, um sie wie einen an einen Block zu „anhängen“. it Block.

it 'whatever test name here' -ForEach $paramSets {	## The parameter set is now represented as the current iteraction in the foreach loop	$paramSet = $_ & "./thing.ps1" @paramSet | should -BeTrue
}

Wenn Sie jetzt den Pester-Test ausführen, wird jeder Parametersatz im Array an den übergeben it Blockieren Sie die Ausführung von drei Tests, indem Sie nur einen einzigen definieren it Block! Viel effizienter.

Den PowerShell-Testcode trocken halten

Jeder Softwareentwickler kennt das Konzept DRY-Methode. Kurz gesagt: Es wiederholt sich einfach nicht. Es bedeutet, den Code so weit wie möglich nicht zu duplizieren. Warum? Für Wartbarkeit.

Stellen Sie sich vor, Sie müssten 100 Datenbankeinträge erstellen und Sie sehen Code wie diesen:

New-Record -Table 'xxxx' -Fields @{ name = 'whatever1' }
New-Record -Table 'xxxx' -Fields @{ name = ' whatever2' }
New-Record -Table 'xxxx' -Fields @{ name = ' whatever3' }
New-Record -Table 'xxxx' -Fields @{ name = ' whatever4' }
New-Record -Table 'xxxx' -Fields @{ name = ' whatever5' }
New-Record -Table 'xxxx' -Fields @{ name = ' whatever6' }
## to 100

Das sind 100 Codezeilen, die leicht auf vier reduziert werden könnten, um den Code zu vereinfachen und die Wartbarkeit zu verbessern.

$whateverCount = 100
for ($i = 1; $i -lt $whateverCount; $i++) {	New-Record -Table 'xxxx' -Fields @{ name = "whatever$i" }
}

Dadurch wurde nicht nur der Code vereinfacht, Sie können auch eine Million Datensätze erstellen, indem Sie einfach eine Zahl ändern. Das ist TROCKEN.

Warum schreiben dann manche Leute solche Pester-Tests?

describe 'some script' {	it 'creates a thing given the parameters to do so' {	./thing.ps1 -Create -Name 'xxx'	}	it 'sets a thing given the parameters to do so' {	./thing.ps1 -Set -Name 'xxx'	}	it 'removes a thing given the parameters to do so' {	./thing.ps1 -Remove -Name 'xxx'	}
}

Wenn Sie neu im Testen sind oder die Dokumentation von Pester wirklich lesen, werden Sie feststellen, dass viele Beispiele so aufgebaut sind. Und daran ist absolut nichts auszusetzen! Wenn Sie eine kleine Handvoll Tests für einfache Skripte erstellen, funktioniert es einwandfrei. Aber es hat mich immer gestört.

Auch wenn es nicht so offensichtlich ist, machen Sie im Grunde das Gleiche; Duplizierung der Funktionalität. Aber Pester-Tests trocken zu machen, ist nicht ganz so einfach wie die Einführung eines for Schleife und Hinzufügen einer einzelnen Variablen. Sie müssen sie so strukturieren, dass sie die domänenspezifische Sprache (DSL) von Pester verwenden.

Bessere Testabdeckung

Das Schreiben von Tests wie PowerShell-Code ist wirklich eine Kunst. Es gibt viele Möglichkeiten, die gleiche Aktion auszuführen, jede mit Vor- und Nachteilen. Wenn Sie Tests für große PowerShell-Skripte schreiben, können Sie verrückt werden und jede … kleine … Minute … jedes Detail testen. Oder Sie können den agilen Weg gehen und eine kleine Reihe von Tests erstellen, die die meisten Situationen abdecken, und einfach Tests hinzufügen, wenn Sie feststellen, dass das Skript fehlschlägt, wenn es in freier Wildbahn verwendet wird.

Wie findet man einen Kompromiss zwischen beiden? Datengesteuerte Tests!

Mit datengesteuerten Tests können Sie jede einzelne Art und Weise definieren, wie Ihr Skript aufgerufen werden kann, indem Sie jeden möglichen Parametersatz vorab in einem Array definieren.

So führen Sie die datengesteuerten Tests von Pester durch

Nachdem Sie nun mit datengesteuerten Tests vertraut sind, wollen wir uns nun mit einer Methode befassen, die ich persönlich verwende, um eine überschaubare Testabdeckung für meinen PowerShell-Code sicherzustellen.

  1. Definieren Sie die obligatorischen Parameter in einer Hashtabelle.
    $mandatoryParameters = @{ EndpointApiKey = (ConvertTo-SecureString -String 'apikeyhere' -AsPlainText -Force) PasswordName = 'namehere' NewPassword = (ConvertTo-SecureString -String 'passwordhere' -AsPlainText -Force)
    }

    Wenn Sie obligatorische Parameter haben, wissen Sie, dass Sie diese bei jeder Ausführung an das Skript übergeben müssen. Definieren Sie sie daher zuerst.

  2. Erstellen Sie ein Array von Hashtabellen, einschließlich der beiden Parametersatz-Hashtabellen Und der Testname.
    $mandatoryParameters = @{ EndpointApiKey = (ConvertTo-SecureString -String 'apikeyhere' -AsPlainText -Force) PasswordName = 'namehere' NewPassword = (ConvertTo-SecureString -String 'passwordhere' -AsPlainText -Force)
    }
    $parameterSets = @( @{ label = 'all mandatory parameters' parameter_set = $mandatoryParameters } @{ label = 'specific EndpointUri' parameter_set = $mandatoryParameters + @{ 'EndpointUri' = 'https://endpointhere' } }
    )

    Beachten Sie, dass ich, anstatt die obligatorischen Parameter zu replizieren, einfach weitere Parameter zum Parametersatz im hinzufüge parameter_set Schlüssel.

  3. Erstellen Sie den Hauptteil context Block, der für alle Situationen gilt.
    context 'Global' {	## do whatever in here that depends on the script
    }
  4. Ein … kreieren context Block für jede Umgebungssituation.
    context 'when the file exists' {
    }
    context 'when the file does not exist' {
    }
  5. Beschränken Sie die an jeden Kontext übergebenen Parametersätze, indem Sie sie vorher herausfiltern.
    context 'when the file exists' {	$ctxParameterSets = $parameterSets.where({ $_.parameter_set.ContainsKey('EndpointUri'') })
    }
  6. Erstellen it Blöcke, die alle Tests darstellen, die Sie durchführen müssen.
    it 'passes the expected URI to the API to update the password : <_.label>' -ForEach $parameterSets { & "$PSScriptRootthing.ps1" @parameter_set | should....
    }

    In Pester v5 können Sie eine Zeichenfolge in den Testnamen einfügen. Ich verwende einen Schlüssel aus dem zuerst definierten Array namens „label“, um eine Beschreibung dieses Parametersatzes einzufügen.

Sobald ich alle Tests auf diese Weise erstellt habe, kann ich einfach Parametersätze zum Array hinzufügen, wodurch automatisch alle benötigten Tests ausgeführt werden!

Advertisement