Structurer les tests PowerShell avec Pester — une méthode pratique pour rendre les scripts d'exploitation plus difficiles à casser
· Mis à jour le: · Go Komura · PowerShell, Pester, Windows, Tests, Automatisation, CI, Réutilisation de l'existant
1. Ce qu’il faut comprendre en premier
Un script PowerShell commence en général comme l’automatisation d’une petite tâche.
Collecter des fichiers. Rechercher dans des journaux. Générer un CSV. Déplacer d’anciens fichiers. Vérifier l’état d’un service.
Tant qu’il s’agit de quelques dizaines de lignes, on peut vérifier le fonctionnement à l’œil nu. Mais à mesure que le script continue d’être utilisé en production, des évolutions de ce genre s’y ajoutent :
- Ajouter des dossiers cibles
- Ajouter des conditions d’exclusion
- Modifier les colonnes du CSV
- Archiver avant suppression
- Exécuter depuis le Planificateur de tâches ou la CI
- Notifier en cas d’erreur
À ce stade, « ça a fonctionné une fois sur mon poste » ne suffit plus.
Le côté effrayant de PowerShell est indissociable de sa commodité. Les opérations de simple lecture peuvent s’essayer sans risque, mais pour des traitements comme la suppression, le déplacement, l’écrasement, le redémarrage d’un service ou le changement de droits, la moindre erreur de condition peut se transformer en incident.
C’est là qu’intervient Pester, le framework de test pour PowerShell. Plutôt que de couvrir l’ensemble des fonctionnalités de Pester, cet article détaille une démarche de « structuration des tests » qui rend, en pratique, les scripts PowerShell existants plus difficiles à casser.
Les tests PowerShell ne servent pas seulement à écrire du code propre. Ce sont des outils pour réduire l’inquiétude avant un changement et pour vérifier, preuves à l’appui, après ce changement.
Le code présenté dans cet article est publié sur GitHub sous la forme d’un ensemble complet d’exemples exécutables avec Invoke-Pester (le script testé, les tests Pester et un script d’exécution pour la CI).
pester-powershell-test-maintenance - komurasoft-blog-samples (GitHub)
2. Que protège-t-on avec Pester ?
Ajouter Pester ne rend pas tout sûr automatiquement. La première chose à décider est : « que doivent protéger les tests ? »
Pour les scripts d’exploitation PowerShell, prioriser ces quatre points est généralement efficace.
| Ce qu’on veut protéger | Ce que le test vérifie |
|---|---|
| Logique de condition | Quels fichiers, lignes, utilisateurs ou services sont ciblés |
| Forme de la sortie | Noms de colonnes du CSV, propriétés de la valeur de retour, nombre d’éléments |
| Étape avant une opération dangereuse | Si les cibles de suppression, de déplacement ou d’arrêt correspondent à l’intention |
| Dépendances externes | Comportement du système de fichiers, des API, de l’exécution de commandes, des dates/heures, des variables d’environnement |
Ce qu’on veut tester en premier, en particulier, n’est pas le traitement de suppression lui-même, mais le traitement qui sélectionne les cibles à supprimer.
Par exemple, pour un script qui supprime d’anciens journaux, ne commencez pas par tester Remove-Item : testez d’abord « quels journaux sont sélectionnés comme cibles ».
Cette séparation facilite grandement les tests.
Fonction qui collecte les cibles
↓
Étape qui vérifie et enregistre les cibles
↓
Étape de modification : déplacement, suppression, notification, etc.
Structurer les tests PowerShell ne consiste pas à refondre immédiatement l’ensemble d’un script existant. Commencez par extraire dans une fonction la partie de décision qui précède un traitement dangereux, puis vérifiez sa valeur de retour avec Pester.
3. Aligner les versions
Cet article part du principe que vous utilisez Pester v5.
Sur d’anciens environnements Windows, Pester peut déjà être installé, mais dans sa branche v3. Plutôt que d’utiliser tel quel ce qui est déjà présent dans l’environnement, commencez par vérifier la version.
Get-Module Pester -ListAvailable |
Sort-Object Version -Descending |
Select-Object Name, Version, Path
Pour une nouvelle installation, installez-le depuis la PowerShell Gallery.
Install-Module -Name Pester -Scope CurrentUser -Force -SkipPublisherCheck
Import-Module Pester
Get-Module Pester
En travail d’équipe, vérifiez que la version de Pester ne diverge pas entre le poste de développement, le serveur de build et l’environnement d’exécution des tâches.
Une source fréquente de confusion dans les tests PowerShell n’est pas un problème de code, mais un écart de version du test runner.
En particulier, d’anciens articles ou notes internes peuvent encore contenir une syntaxe antérieure à Pester v4. Si vous mettez en place de nouveaux tests, s’aligner sur la syntaxe v5 les rendra plus faciles à relire par la suite.
4. Décider où placer les fichiers
Avec Pester, il est courant de nommer les fichiers de test *.Tests.ps1.
Voici la disposition minimale :
scripts/
Get-OldLogFile.ps1
Get-OldLogFile.Tests.ps1
Pour un projet un peu plus grand, séparez src et tests :
src/
public/
Get-OldLogFile.ps1
Remove-OldLogFile.ps1
tests/
public/
Get-OldLogFile.Tests.ps1
Remove-OldLogFile.Tests.ps1
Les deux conviennent. Ce qui compte, c’est de fixer une convention.
- Un fichier de test par fonction
- Les noms de fichiers de test se terminent par
.Tests.ps1 - Charger le code testé toujours de la même façon
- Ne pas trop mélanger tests unitaires et tests d’intégration
Au départ, placer le .ps1 cible juste à côté de son .Tests.ps1 suffit amplement.
5. Exécuter un test minimal
Préparons d’abord une fonction simple, Get-OldLogFile.ps1.
function Get-OldLogFile {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string] $Path,
[int] $Days = 30,
[string] $Filter = '*.log',
[datetime] $Now = (Get-Date)
)
if (-not (Test-Path -LiteralPath $Path -PathType Container)) {
throw "Folder not found: $Path"
}
$limit = $Now.AddDays(-1 * $Days)
Get-ChildItem -LiteralPath $Path -Filter $Filter -File |
Where-Object { $_.LastWriteTime -lt $limit } |
Sort-Object -Property LastWriteTime |
Select-Object FullName, Name, Length, LastWriteTime
}
Ici, pour faciliter les tests, $Now est reçu comme paramètre.
Si la fonction appelle directement Get-Date à chaque exécution, le résultat varie selon le jour où le test est lancé. En transformant la date en paramètre, on peut fixer une condition comme « les fichiers plus anciens que 30 jours au 1er juin 2026 » et la tester telle quelle.
Ensuite, écrivons les tests dans Get-OldLogFile.Tests.ps1.
BeforeAll {
. $PSScriptRoot\Get-OldLogFile.ps1
}
Describe 'Get-OldLogFile' {
BeforeEach {
$script:Root = Join-Path $TestDrive 'logs'
New-Item -ItemType Directory -Path $script:Root -Force | Out-Null
$oldLog = Join-Path $script:Root 'old.log'
$newLog = Join-Path $script:Root 'new.log'
$oldTxt = Join-Path $script:Root 'old.txt'
Set-Content -LiteralPath $oldLog -Value 'old log' -Encoding UTF8
Set-Content -LiteralPath $newLog -Value 'new log' -Encoding UTF8
Set-Content -LiteralPath $oldTxt -Value 'old text' -Encoding UTF8
(Get-Item -LiteralPath $oldLog).LastWriteTime = [datetime]'2026-05-01T00:00:00'
(Get-Item -LiteralPath $newLog).LastWriteTime = [datetime]'2026-05-31T00:00:00'
(Get-Item -LiteralPath $oldTxt).LastWriteTime = [datetime]'2026-05-01T00:00:00'
}
It 'ne retourne que les fichiers .log plus anciens que le nombre de jours indiqué' {
$result = Get-OldLogFile `
-Path $script:Root `
-Days 30 `
-Now ([datetime]'2026-06-01T00:00:00')
$result | Should -HaveCount 1
$result[0].Name | Should -Be 'old.log'
}
It 'échoue si le dossier est introuvable' {
{ Get-OldLogFile -Path (Join-Path $TestDrive 'missing') } |
Should -Throw
}
}
Exécutons-le.
Invoke-Pester -Output Detailed .\Get-OldLogFile.Tests.ps1
Le $TestDrive utilisé ici est une zone temporaire fournie par Pester pour les tests. Au lieu de toucher au vrai C:\Logs ou à un dossier partagé, on travaille uniquement avec des fichiers créés à l’intérieur du test. Pour les scripts PowerShell impliquant des opérations sur les fichiers, prendre l’habitude d’utiliser $TestDrive en premier lieu est la démarche sûre.
6. Écrire les noms de test comme des spécifications
La chaîne de caractères que l’on écrit dans le It de Pester n’est pas une simple description : pour quiconque la relira plus tard, elle devient une petite spécification.
Par exemple, un nom comme celui-ci est un peu faible :
It 'fonctionne' {
# ...
}
On ne sait pas ce qui est censé fonctionner.
En pratique, un nom qui inclut la condition et le résultat attendu est bien plus facile à lire.
It 'ne retourne que les fichiers .log plus anciens que le nombre de jours indiqué' {
# ...
}
It 'exclut les fichiers exactement à la date limite' {
# ...
}
It 'échoue si le dossier est introuvable' {
# ...
}
Un bon nom de test se révèle utile au moment d’un échec. Quand le journal de la CI affiche ceci, on comprend immédiatement ce qui a cassé :
[-] Get-OldLogFile.exclut les fichiers exactement à la date limite
Le nom d’un test est une note laissée à son futur soi.
7. Ajouter une condition limite
Le Get-OldLogFile vu plus haut détermine qu’un fichier est ancien avec cette condition :
$_.LastWriteTime -lt $limit
Comme il s’agit de -lt, un fichier dont la date correspond exactement à la date limite est exclu.
Cette décision est minime, mais elle compte en pratique, car le nombre de fichiers concernés change selon qu’on retient « plus ancien que 30 jours » ou « 30 jours ou plus, inclus ».
Ajoutons la condition limite aux tests.
It 'exclut les fichiers exactement à la date limite' {
$border = Join-Path $script:Root 'border.log'
Set-Content -LiteralPath $border -Value 'border log' -Encoding UTF8
(Get-Item -LiteralPath $border).LastWriteTime = [datetime]'2026-05-02T00:00:00'
$result = Get-OldLogFile `
-Path $script:Root `
-Days 30 `
-Now ([datetime]'2026-06-01T00:00:00')
$result.Name | Should -Not -Contain 'border.log'
}
Il ne s’agit pas d’écrire le plus de tests possible. Mais les traitements comportant une limite — dates, valeurs numériques, nombre d’éléments, droits, motifs de noms de fichiers — sont ceux où les tests apportent le plus de valeur.
8. Fixer la forme de la valeur de retour
Dans les scripts PowerShell, la forme de la valeur de retour peut changer sans qu’on s’en aperçoive.
Au départ, la fonction retournait directement un FileInfo.
Puis un Select-Object a été ajouté en cours de route.
Puis les noms de colonnes ont été modifiés pour la sortie CSV.
Ce genre de changement affecte le traitement en aval ; tester les propriétés de la valeur de retour permet donc de détecter un changement inattendu.
It 'retourne les propriétés utilisées par le traitement suivant' {
$result = Get-OldLogFile `
-Path $script:Root `
-Days 30 `
-Now ([datetime]'2026-06-01T00:00:00')
$propertyNames = $result[0].PSObject.Properties.Name
$propertyNames | Should -Contain 'FullName'
$propertyNames | Should -Contain 'Name'
$propertyNames | Should -Contain 'Length'
$propertyNames | Should -Contain 'LastWriteTime'
}
Pour une fonction dont la sortie alimente un export CSV ou un rapport, les noms de colonnes font partie de la spécification, tout autant que les valeurs.
Vérifiez non seulement que « ça fonctionne », mais aussi que « le résultat est renvoyé dans la forme attendue par l’étape suivante ».
9. Séparer la suppression de la sélection des cibles
Passons maintenant au traitement de suppression. Commençons par un mauvais exemple.
Get-ChildItem C:\Logs -Filter *.log -File |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -Force
C’est court et pratique, mais difficile à tester. Comme la sélection des cibles et la suppression sont enchaînées dans un seul pipeline, on ne sait plus vraiment quoi vérifier.
En pratique, on sépare les deux comme ceci :
function Remove-OldLogFile {
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)]
[string] $Path,
[int] $Days = 30,
[datetime] $Now = (Get-Date)
)
$targets = Get-OldLogFile -Path $Path -Days $Days -Now $Now
foreach ($target in $targets) {
if ($PSCmdlet.ShouldProcess($target.FullName, 'Remove old log file')) {
Remove-Item -LiteralPath $target.FullName -Force
}
}
}
Ici, SupportsShouldProcess est ajouté pour que la fonction puisse elle-même accepter -WhatIf.
Remove-OldLogFile -Path C:\Logs -Days 30 -WhatIf
Pour les fonctions PowerShell qui suppriment des éléments, il est plus sûr de permettre autant que possible une répétition à blanc avec -WhatIf.
10. Remplacer les opérations dangereuses par Mock
Avec Pester, Mock permet de remplacer l’exécution réelle d’une commande.
Il n’est pas nécessaire d’exécuter réellement Remove-Item pour tester la logique de suppression.
A-t-elle été appelée dans les situations où elle doit l’être ? N’a-t-elle pas été appelée dans les situations où elle ne doit pas l’être ?
Vérifier cela suffit.
Voici un exemple de Remove-OldLogFile.Tests.ps1 :
BeforeAll {
. $PSScriptRoot\Get-OldLogFile.ps1
. $PSScriptRoot\Remove-OldLogFile.ps1
}
Describe 'Remove-OldLogFile' {
It 'appelle Remove-Item pour les anciens fichiers journaux' {
Mock Get-OldLogFile {
[pscustomobject]@{
FullName = 'C:\Logs\old.log'
Name = 'old.log'
Length = 10
LastWriteTime = [datetime]'2026-05-01'
}
}
Mock Remove-Item {}
Remove-OldLogFile `
-Path 'C:\Logs' `
-Days 30 `
-Now ([datetime]'2026-06-01')
Should -Invoke Remove-Item `
-Times 1 `
-Exactly `
-ParameterFilter { $LiteralPath -eq 'C:\Logs\old.log' }
}
It 'ne fait pas appel à Remove-Item avec WhatIf' {
Mock Get-OldLogFile {
[pscustomobject]@{
FullName = 'C:\Logs\old.log'
Name = 'old.log'
Length = 10
LastWriteTime = [datetime]'2026-05-01'
}
}
Mock Remove-Item {}
Remove-OldLogFile `
-Path 'C:\Logs' `
-Days 30 `
-Now ([datetime]'2026-06-01') `
-WhatIf
Should -Invoke Remove-Item -Times 0
}
}
Dans ces tests, Get-OldLogFile comme Remove-Item sont tous deux mockés, si bien que le vrai C:\Logs\old.log n’a même pas besoin d’exister. Ce qui est examiné, c’est la logique de décision de Remove-OldLogFile :
- S’il y a des cibles, appeler
Remove-Item - Avec
-WhatIf, ne pas appelerRemove-Item - Lors de l’appel, transmettre le chemin voulu
Plus une opération est dangereuse, plus il est prudent de tester les conditions de son appel plutôt que l’exécution elle-même.
11. Ne pas abuser de Mock
Mock est pratique, mais en abuser fait perdre de la valeur aux tests, car tout mocker éloigne trop du comportement réel de PowerShell.
Voici quelques repères.
| Traitement | Recommandation |
|---|---|
| Dates | Les fixer via un paramètre |
| Création de fichiers | Utiliser $TestDrive |
| Suppression, déplacement | Vérifier avec Mock et -WhatIf |
| Appels à une API Web | Mock-er Invoke-RestMethod et similaires |
| Envoi d’e-mails, notifications | Mock-er la commande d’envoi |
| Lecture/écriture de CSV | Créer de petits fichiers réels dans $TestDrive |
Mocker jusqu’à la lecture et l’écriture des fichiers peut faire passer à côté de vrais problèmes d’encodage de caractères, de fins de ligne ou de noms de colonnes.
À l’inverse, des traitements comme la suppression, les notifications, les API externes ou l’arrêt de services ne devraient vraiment pas être exécutés pour de bon.
Séparez « les endroits où utiliser le vrai comportement » des « endroits à mocker ».
12. Rendre les scripts existants testables
Introduire Pester modifie un peu la façon d’écrire les scripts existants. Mais il n’est pas nécessaire de se lancer d’emblée dans un grand changement de conception : des retouches de cette ampleur suffisent pour commencer.
Avant retouche
$limit = (Get-Date).AddDays(-30)
Get-ChildItem C:\Logs -Filter *.log -File |
Where-Object { $_.LastWriteTime -lt $limit } |
Remove-Item -Force
Après retouche
function Get-OldLogFile {
param(
[string] $Path,
[int] $Days = 30,
[datetime] $Now = (Get-Date)
)
$limit = $Now.AddDays(-1 * $Days)
Get-ChildItem -LiteralPath $Path -Filter *.log -File |
Where-Object { $_.LastWriteTime -lt $limit }
}
function Remove-OldLogFile {
[CmdletBinding(SupportsShouldProcess)]
param(
[string] $Path,
[int] $Days = 30,
[datetime] $Now = (Get-Date)
)
Get-OldLogFile -Path $Path -Days $Days -Now $Now |
ForEach-Object {
if ($PSCmdlet.ShouldProcess($_.FullName, 'Remove old log file')) {
Remove-Item -LiteralPath $_.FullName -Force
}
}
}
Les changements ne sont pas considérables.
- La date est devenue un paramètre
- La sélection des cibles est devenue une fonction
- Le traitement de suppression est devenu une fonction séparée
SupportsShouldProcessa été ajouté
Cela seul rend le code bien plus facile à tester.
Pour structurer les tests PowerShell, plutôt que de partir de considérations théoriques de conception, il est plus efficace de rendre remplaçables depuis l’extérieur « la date », « le chemin », « les commandes externes » et « les opérations de modification ».
13. Décider des catégories de tests
Avec Pester, on peut ajouter des tags à Describe, Context et It.
Par exemple, on sépare les tests unitaires rapides des tests d’intégration qui touchent l’environnement réel.
Describe 'Get-OldLogFile' -Tag 'Unit' {
It 'ne retourne que les fichiers .log plus anciens que le nombre de jours indiqué' {
# Test rapide utilisant TestDrive
}
}
Describe 'Log maintenance smoke test' -Tag 'Smoke' {
It 'peut lire le vrai dossier de journaux' {
Test-Path -LiteralPath 'C:\Logs' | Should -BeTrue
}
}
Exécutons uniquement les tests unitaires.
Invoke-Pester -TagFilter Unit
Excluons les tests lents ou dépendants de l’environnement.
Invoke-Pester -ExcludeTagFilter Slow, RequiresAdmin, Network
Sur le terrain, vouloir exécuter tous les tests à chaque fois peut s’avérer intenable dans la durée.
Commencez par faire des tests rapides et sans effet de bord la norme, et séparez par tags les tests dépendants de l’environnement pour les exécuter seulement quand c’est nécessaire.
14. Exécuter en CI
Pester est déjà utile en exécution locale, mais si une équipe gère les scripts collectivement, pouvoir l’exécuter en CI apporte une tranquillité d’esprit supplémentaire.
Préparons par exemple un fichier comme tools/Invoke-ProjectTests.ps1 :
$ErrorActionPreference = 'Stop'
Import-Module Pester
$config = New-PesterConfiguration
$config.Run.Path = @(
Join-Path $PSScriptRoot '..\tests'
)
$config.Run.Exit = $true
$config.Output.Verbosity = 'Detailed'
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = 'JUnitXml'
$config.TestResult.OutputPath = Join-Path $PSScriptRoot '..\test-results.xml'
$config.CodeCoverage.Enabled = $true
$config.CodeCoverage.Path = @(
Join-Path $PSScriptRoot '..\src'
)
$config.CodeCoverage.OutputPath = Join-Path $PSScriptRoot '..\coverage.xml'
Invoke-Pester -Configuration $config
Côté CI, on exécute ce script :
pwsh -NoProfile -File .\tools\Invoke-ProjectTests.ps1
Le point important est de ne pas trop écrire de configuration spécifique à la CI à l’intérieur des fichiers de test eux-mêmes.
Les fichiers de test sont l’endroit où l’on écrit des spécifications. Le format de sortie pour la CI, la couverture de code, le code de sortie, etc. sont plus faciles à organiser en les regroupant dans le script d’exécution.
15. Considérer la couverture comme une carte, pas comme un objectif
Pester peut aussi produire la couverture de code. Mais mieux vaut ne pas trop courir après le chiffre dès le départ, car la couverture n’est pas la qualité des tests elle-même.
Par exemple, appeler une seule fois la fonction d’extraction des cibles de suppression suffit à faire passer ces lignes comme « couvertes ». Mais si les conditions limites et les conditions d’exclusion n’ont pas été vérifiées, cela n’apporte aucune assurance réelle en pratique.
Voici comment utiliser la couverture :
- Repérer les fonctions qui ne sont jamais exercées
- Repérer les branches importantes qui n’ont aucun test
- Prioriser en partant des scripts les plus fréquemment modifiés
- Conserver, via la CI, une trace de l’exécution des tests
Plutôt que de faire monter le chiffre, regardez si « les décisions importantes sont testées ».
16. Ordre d’introduction des tests dans les scripts existants
Pour introduire Pester dans un ensemble de scripts PowerShell existants, il est plus facile d’avancer sans chercher à tout couvrir d’un coup. Voici un ordre recommandé.
1. Choisir les scripts dont l’échec ferait mal
Au départ, ce type de scripts convient bien :
- Ils incluent suppression, déplacement ou écrasement
- Ils tournent tous les jours ou tous les mois
- Ils figurent dans une procédure documentée mais restent la spécialité d’une seule personne
- Ils ont déjà connu une erreur de condition par le passé
- Leur CSV de sortie est utilisé par d’autres activités métier
Commencez par ce qui est utile mais poserait problème s’il venait à casser.
2. Extraire uniquement la partie lecture dans des fonctions
Ce qu’on teste en premier, ce n’est pas le traitement de modification, mais le traitement de lecture.
Lire les journaux
Filtrer les cibles
Compter les éléments
Mettre en forme pour le CSV
Cette partie se teste facilement avec $TestDrive, et les accidents y sont peu probables.
3. Passer la date et le chemin depuis l’extérieur
Quand la date ou le chemin sont figés en dur, les tests deviennent difficiles.
# À éviter
$root = 'C:\Logs'
$limit = (Get-Date).AddDays(-30)
Voici une forme facile à tester.
param(
[string] $Path,
[datetime] $Now = (Get-Date)
)
Le simple fait de pouvoir passer les valeurs depuis l’extérieur améliore considérablement la stabilité des tests.
4. Placer les opérations dangereuses en dernier
Regroupez la suppression et le déplacement à la toute fin.
Construire la liste des cibles
↓
Consigner les cibles dans un journal
↓
Vérifier avec -WhatIf
↓
Exécuter
Vérifiez également dans le même ordre au sein des tests.
17. Difficultés courantes
| Symptôme | Cause | Solution |
|---|---|---|
| Passe en local mais échoue en CI | Le répertoire courant diffère | Se baser sur $PSScriptRoot |
| Le résultat varie selon le jour | Get-Date est utilisé directement |
Prévoir un paramètre du type -Now |
| Le test risque de supprimer de vrais fichiers | Un vrai dossier est utilisé | Utiliser $TestDrive et Mock |
| Le Mock n’a aucun effet | Une frontière de module ou de portée diffère | Vérifier -ModuleName et la méthode de chargement |
| On ne sait pas jusqu’où tester | La spécification n’est pas répartie en fonctions | Séparer sélection des cibles, mise en forme et modification |
| Les tests sont lents | Un service externe ou le réseau est sollicité | Mock-er les dépendances externes dans les tests unitaires |
| Le nom du test ne dit rien | Un nom du type It 'works' |
Inclure la condition et le résultat attendu dans le nom |
Ce qui ressemble à un problème de Pester provient souvent, en réalité, de la structure même du script.
Les parties difficiles à tester sont généralement aussi celles qui cassent le plus facilement en exploitation.
18. Règles à fixer pour la mise en place des tests
Si une équipe gère des scripts PowerShell collectivement, fixez des règles avant de vous soucier des détails de style.
En voici des exemples.
- Les fichiers de test sont nommés
*.Tests.ps1 - Le code testé est chargé depuis
$PSScriptRoot - Les tests d’opérations sur les fichiers utilisent
$TestDrive - La suppression, le déplacement, les notifications et les appels d’API sont mockés par principe
- Les dates sont paramétrées pour pouvoir être fixées
DescribeouItporte des tags commeUnit,Smoke,RequiresAdmin- La CI exécute
Unitpar défaut - Les fonctions de modification reçoivent
SupportsShouldProcesschaque fois que possible - Les anomalies passées sont conservées sous forme de tests de non-régression
Trop de règles finissent par ne plus être suivies. Au départ, ces trois-là suffisent amplement :
Utiliser TestDrive
Fixer la date
Mocker les opérations dangereuses
Respecter uniquement ces trois règles rend déjà les tests PowerShell nettement plus stables.
19. Décider aussi de ce qu’on ne teste pas
Dans la mise en place des tests, « ce qu’on ne teste pas » compte autant que « ce qu’on teste ».
Par exemple, mieux vaut ne pas s’acharner à vérifier ces points-ci dans des tests unitaires :
- Que
Get-ChildItemde Windows lui-même fonctionne correctement - Que
Remove-Itemsupprime réellement les fichiers - Le comportement interne des commandes standard de PowerShell
- Qu’une API externe réponde toujours
- Qu’un partage réseau soit toujours disponible
Ce qu’il faut tester, ce sont vos propres décisions.
- Selon quelle condition un élément devient une cible
- Quel chemin est transmis
- Quelles colonnes sont produites en sortie
- Comment un échec est géré
- Si une opération dangereuse peut être répétée à blanc
Séparez les endroits où vous faites confiance aux commandes standard de ceux où vous protégez votre propre logique.
20. Conclusion
PowerShell est un outil pratique qui permet d’automatiser rapidement des tâches quotidiennes. Mais un script utilisé longtemps en production voit sa responsabilité s’alourdir peu à peu. Ce qui n’était au départ qu’une commande d’une ligne pour un usage personnel finit par devenir un traitement d’exploitation qui tourne tous les jours et affecte le travail d’autres personnes ainsi que les données métier.
Structurer les tests avec Pester, c’est le travail qui protège le script à mesure de cette évolution.
Récapitulons les points clés.
- Tester d’abord la sélection des cibles
- Rendre la date et le chemin transmissibles depuis l’extérieur
- Confiner les opérations sur les fichiers dans
$TestDrive Mock-er la suppression, le déplacement, les notifications et les appels d’API- Rendre les fonctions de modification répétables à blanc avec
-WhatIf - Écrire les noms de test de façon à ce qu’on puisse les lire comme des spécifications
- En CI, faire tourner en premier les tests rapides et sans effet de bord
L’exploitation sûre de PowerShell ne consiste pas à mettre en place d’un coup un grand dispositif.
Découper en petites fonctions. Écrire de petits tests. Rendre possible une vérification avant tout traitement dangereux.
Cette accumulation fait progressivement passer PowerShell d’un « script pratique mais un peu effrayant » à un « outil métier qu’on peut vérifier à chaque changement ».
Liens de référence
- L’ensemble complet du code d’exemple de cet article (le script testé, les tests Pester et un script d’exécution pour la CI) https://github.com/gomurin0428/komurasoft-blog-samples/tree/main/pester-powershell-test-maintenance
- Pester Quick Start
- Pester Installation and Update
- Pester File placement and naming
- Pester TestDrive
- Pester Mocking
- Pester Tags
- Pester Configuration
- Pester Test Results
- Pester Code Coverage
- PowerShell Gallery: Pester
- PowerShell Documentation - Microsoft Learn
Articles connexes
- Les bases des commandes PowerShell — les premières opérations à connaître et comment les utiliser en toute sécurité
- PowerShell avancé — automatiser en toute sécurité l’investigation, l’archivage et le reporting des journaux
- Recueil de commandes PowerShell pratiques — enrichir les petits outils utilisés au quotidien
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Gestion des erreurs et conception des nouvelles tentatives sous PowerShell — du piège du try/catch aux bonnes pratiques d'exit code et de retry
Cet article présente, du point de vue pratique, la différence entre erreurs terminales et non terminales sous PowerShell, le piège du try...
Politique d'exécution PowerShell et signature de scripts — Guide pratique pour sortir de l'exploitation « on colmate avec Bypass »
La politique d'exécution de PowerShell est « un dispositif de sécurité, pas une frontière de sécurité ». Cet article présente les différe...
Comment exécuter PowerShell depuis C# (CSharp) et récupérer le résultat sous forme d'objets
Comment lancer PowerShell depuis C# et récupérer le résultat sous forme de PSObject plutôt que de chaînes de caractères : un tour d'horiz...
Recueil de commandes PowerShell pratiques — enrichir les petits outils utilisés au quotidien
Panorama des commandes PowerShell pratiques pour le travail quotidien : quand utiliser Measure-Object, Group-Object, Select-String, Compa...
PowerShell avancé — automatiser en toute sécurité l'investigation, l'archivage et le reporting des journaux
Procédure pratique pour mener en toute sécurité, avec des scripts PowerShell, l'investigation des journaux, le reporting CSV, l'archivage...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Que faut-il tester en premier avec Pester ?
- Ne commencez pas par le traitement de suppression lui-même, mais par le traitement qui sélectionne les cibles à supprimer (la sélection des cibles). Pour les scripts d'exploitation, il est efficace de prioriser ces quatre points : « la logique de condition », « la forme de la sortie », « l'étape juste avant une opération dangereuse » et « les dépendances externes ». Il n'est pas nécessaire de refondre immédiatement l'intégralité d'un script existant : commencez par extraire dans une fonction la logique de décision qui précède une opération dangereuse, puis vérifiez sa valeur de retour avec Pester.
- Qu'est-ce que $TestDrive dans Pester ?
- $TestDrive est une zone temporaire fournie par Pester pour les tests. Il permet de vérifier des opérations sur les fichiers en utilisant uniquement des fichiers créés à l'intérieur du test, sans toucher au vrai C:\Logs ni à un dossier partagé. Pour les tests de scripts PowerShell impliquant des opérations sur les fichiers, prendre l'habitude d'utiliser $TestDrive dès le départ permet d'éviter des accidents comme la suppression involontaire de vrais fichiers.
- Jusqu'où faut-il utiliser Mock dans Pester ?
- Remplacez par un Mock les opérations qu'il ne faut jamais exécuter réellement, comme la suppression, le déplacement, les appels à une API Web, ou l'envoi d'e-mails et de notifications ; fixez les dates via un paramètre ; et pour la création de fichiers ou la lecture/écriture de CSV, il est préférable de créer de petits fichiers réels dans $TestDrive. Si vous mockez absolument tout, vous vous éloignez trop du comportement réel de PowerShell et vous risquez de passer à côté de problèmes d'encodage de caractères, de fins de ligne ou de noms de colonnes. Le point clé est donc de séparer « les endroits où utiliser le vrai comportement » des « endroits à mocker ».
- Pourquoi un test Pester qui passe en local échoue-t-il en CI ?
- La cause la plus fréquente est une différence de répertoire courant, que l'on résout en chargeant le script testé à partir de $PSScriptRoot. Si les résultats varient selon le jour, c'est généralement parce que Get-Date est utilisé directement dans le code : fixez la date via un paramètre du type -Now. Si un Mock ne semble pas prendre effet, suspectez une différence de frontière de module ou de portée (scope), et vérifiez -ModuleName ainsi que la méthode de chargement. Ce qui ressemble à un problème de Pester provient en réalité souvent de la structure même du script.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.
Liens publics