Moduł ISEPester – testujemy w PowerShell ISE

PesterO testach w PowerShellu pisałem już na tym blogu kilka razy. Dziś skupię się nie na samych testach, ich tworzeniu i zastosowaniach, ale na module, który “popełniłem” inspirowany sesją przygotowaną niemal dwa lata temu przez Jakuba Jareša, człowieka w znacznej mierze odpowiedzialnego za moduł Pester. W sesji tej Jakub demonstrował jak w VS Code uruchamiać pojedynczy test, czy blok Context/ Describe. Po ustaleniu, że wtyczka do VS Code korzysta po prostu z odpowiedniego rodzaju filtrów w trakcie uruchamiania testów, nie pozostało mi nic innego, jak tylko przenieść te możliwości do mojego ulubionego edytora, PowerShell ISE.

Zacznijmy od tego, że rozwiązanie to dostępne jest jedynie w wersji piątej Pestera. Jeśli z jakiegoś powodu zmuszeni jesteś korzystać z wersji czwartej – to niestety, metoda opisywana na nic się Wam nie przyda. Sam moduł nazywa się ISEPester i znajdziecie go w galerii PowerShella i na GitHubie.

Jeśli jednak korzystać możecie z wersji piątej, to pewnie wiecie, że Pester od tej wersji korzysta ze specjalnie spreparowanego obiektu konfiguracji. Owszem, w najprostszych przypadkach możemy nadal test uruchomić bez niego, przykładowo uruchomimy plik z testem wykorzystując jedną z dwu składni uzyskując dokładnie ten sam efekt:

Invoke-Pester -Path .\Connect-EWSService.tests.ps1 -Output Detailed
$konfiguracja = [PesterConfiguration]@{
Run = @{
Path = '.\Connect-EWSService.tests.ps1'
}
Output = @{
Verbosity = 'Detailed'
}
}
Invoke-Pester -Configuration $konfiguracja

Uproszczona składnia jednak to rozwiązanie znacznie ograniczające nasze możliwości. Tak jest też i w tym przypadku – bez obiektu konfiguracji ani rusz.

Jak taki obiekt utworzyć? Najlepsza metoda to skorzystać z właściwości statycznej. Dalej możemy zmieniać właściwości tak utworzonego obiektu. Co ciekawe, każda z właściwości posiada informację zarówno o wartości bieżącej (Value), domyślnej (Default), jak i pełny opis (wraz z dozwolonymi wartościami, jeśli dany parametr akceptuje tylko jedną ze zdefiniowanych wartości:

$konfiguracja = [PesterConfiguration]::Default
$konfiguracja.Output.Verbosity = 'Detailed'
$konfiguracja.Output.Verbosity
<#
Na wyjściu...
Default Description Value
------- ----------- -----
Normal The verbosity of output, options are None, Normal, Detailed and Diagnostic. Detailed
#>

Nas interesować będzie szczególnie jedno ustawienie, którego nie da się modyfikować wykorzystując składnię uproszczoną: filtrowanie. Otóż efekt uruchomienia pojedynczego bloku It/Context/Describe wymaga zdefiniowania filtru obejmującego konkretną linię kodu, w konkretnym pliku. Ważne, by linia ta zawierała właśnie jedno z trzech wymienionych poleceń.

Zanim jednak dobierzemy się do filtrów, musimy ustalić plik testu, który zamierzamy uruchomić. W PowerShell ISE to bajecznie proste: wbudowany obiekt $psISE posiada właściwość CurrentFile. Ta zaś zawiera informację o stanie edytora (przyda nam się to nieco później) oraz o ścieżce do aktualnie otwartego pliku:

ISEPester-Otwarty-Plik

Jeśli mamy więc otwarty plik, to sprawdzić musimy jeszcze:

  • czy plik ten istnieje na dysku (inaczej nie możemy go uruchomić w ten sposób)
  • czy w edytorze wybrano jakąkolwiek linię
  • czy jesteśmy w stanie określić linię, którą wybrano

Jeśli powyższe warunki są spełnione to wiemy już, jaki test musimy uruchomić. Dodatkowo możemy uprzedzić użytkownika, jeśli próbuje uruchomić test w pliku ze zmianami, które nie zostały jeszcze zapisane:

if (
($file = $psISE.CurrentFile) -and
(Test-Path -LiteralPath $file.FullPath) -and
($line = $file.Editor.CaretLineText) -and
($lineNumber = $file.Editor.CaretLine)
) {
if (-not $file.IsSaved) {
Write-Warning -Message "File $($file.FullPath) is not saved - working on current copy on disk!"
}
$config = [PesterConfiguration]@{
Run = @{
Path = $file.FullPath
}
}
# ...
}

Wiemy, że filtr musi zawierać linię z odpowiednim poleceniem. Możliwości są trzy: kursor jest na takiej właśnie linii, kursor umieszczono wewnątrz testu (dowolne miejsce w bloku It), lub wersja samobójcza, gdy kursor nie znajduje się wewnątrz testu (lub po prostu nie znajduje się w pliku z testami).

Zacznijmy od opcji najprostszej: w takim przypadku mamy już komplet informacji, możemy więc od razu zdefiniować filtr:

if ($line -match '\s*(Describe|Context|It)') {
$config.Filter.Line = '{0}:{1}' -f $file.FullPath, $lineNumber
}

W drugim przypadku z pomocą przyjdzie nam AST: odszukamy blok It, w którym znajduje się nasz kursor i to linię w której blok się ten znajduje przekażemy do filtra:

$parsedTestFile = [System.Management.Automation.Language.Parser]::ParseFile($file.FullPath, [ref]$null, [ref]$null)
$myItBlock = $parsedTestFile.FindAll(
{
param (
$Ast
)
$Ast.CommandElements -and
$Ast.CommandElements[0].Value -eq 'It' -and
$Ast.Extent.StartLineNumber -le $lineNumber -and
$Ast.Extent.EndLineNumber -ge $lineNumber
},
$true
)
if ($myItBlock) {
$config.Filter.Line = '{0}:{1}' -f $file.FullPath, $myItBlock[0].Extent.StartLineNumber
}

W ostatnim przypadku nie jesteśmy w stanie ustalić, co autor miał na myśli – dajemy więc za wygraną.

Oprócz uruchamiania testów, moduł umożliwi nam też zmianę sposobu wyświetlania wyników – dla przykładu, możemy zdecydować, by Pester wyświetlił dane diagnostyczne:

Set-ISEPesterConfiguration -Verbosity Diagnostic

Na koniec – krótki filmik o korzystaniu z ISEPester. Smacznego!

~ - autor: Bartek Bielawski w dniu 12 lipca, 2022.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s

 
%d blogerów lubi to: