Question [Fonction]Génération de jeu de tests

Plus d'informations
il y a 11 ans 3 semaines #14435 par Laurent Dardenne
Voici une fonction générant le produit cartésien de chaque jeux de paramètre d'une commande, permettant ainsi d'automatiser certains tests :
[code:1]
function New-TestSetParameters{
#Génére le produit cartésien de chaque jeux de paramètre d'une commande
#adapted from : #http://makeyourownmistakes.wordpress.com/2012/04/17/simple-n-ary-product-generic-cartesian-product-in-powershell-20/
[CmdletBinding(DefaultParameterSetName=\"Nammed\"«»)]
[OutputType([System.String])]
param (

[parameter(Mandatory=$True,ValueFromPipeline=$True)]
[ValidateNotNull()]
[System.Management.Automation.CommandInfo] $CommandName,

[ValidateNotNullOrEmpty()]
[Parameter(Position=0,Mandatory=$false)]
[string[]] $ParameterSetNames='__AllParameterSets',

[ValidateNotNullOrEmpty()]
[Parameter(Mandatory=$false,ParameterSetName=\"All\"«»)]
[string[]] $Exclude,

[Parameter(ParameterSetName=\"All\"«»)]
[switch] $All
)

begin {
# Common parameters
# [System.Management.Automation.Internal.CommonParameters].GetProperties()|Foreach {$_.name}
# -Verbose (vb) -Debug (db) -WarningAction (wa)
# -WarningVariable (wv) -ErrorAction (ea) -ErrorVariable (ev)
# -OutVariable (ov) -OutBuffer (ob) -WhatIf (wi) -Confirm (cf)

function getValue{
if ($Value -eq $false)
{
Write-Debug \"Switch is `$false, dont add the parameter name : $Result.\"
return \"$result\"
}
else
{
Write-Debug \"Switch is `$true, add only the parameter name : $result$Bindparam\"
return \"$result$Bindparam\"
}
}#getValue

function AddToAll{
param (
[System.Management.Automation.CommandParameterInfo] $Parameter,
$currentResult,
$valuesToAdd
)
Write-Debug \"Treate '$($Parameter.Name)' parameter.\"
Write-Debug \"currentResult =$($currentResult.Count -eq 0)\"
$Bindparam=\" -$($Parameter.Name)\"
#Récupère une information du type du paramètre et pas la valeur liée au paramètre
$isSwitch=($Parameter.parameterType.FullName -eq 'System.Management.Automation.SwitchParameter')
Write-Debug \"isSwitch=$isSwitch\"
$returnValue = @()
if ($valuesToAdd -ne $null)
{
foreach ($value in $valuesToAdd)
{
Write-Debug \"Add Value : $value \"
if ($currentResult -ne $null)
{
foreach ($result in $currentResult)
{
if ($isSwitch)
{ $returnValue +=getValue }
else
{
Write-Debug \"Add parameter and value : $result$Bindparam $value\"
$returnValue += \"$result$Bindparam $value\"
}
}#foreach
}
else
{
if ($isSwitch)
{ $returnValue +=\"$($CommandName.Name)$(getValue)\" }
else
{
Write-Debug \"Add parameter and value :: $Bindparam $value\"
$returnValue += \"$($CommandName.Name)$Bindparam $value\"
}
}
}
}
else
{
Write-Debug \"ValueToAdd is `$Null :$currentResult\"
$returnValue = $currentResult
}
return $returnValue
}#AddToAll
}#begin

process {

foreach ($Set in $CommandName.ParameterSets)
{
if (-not $All -and ($ParameterSetNames -notcontains $Set.Name))
{ continue }
elseif ( $All -and ($Exclude -contains $Set.Name))
{
Write-Debug \"Exclude $($Set.Name) \"
continue
}

$returnValue = @()
Write-Debug \"Current set name is $($Set.Name) \"
Write-Debug \"Parameter count=$($Set.Parameters.Count) \"
#Write-Debug \"$($Set.Parameters|Select name|out-string) \"
foreach ($parameter in $Set.Parameters)
{
$Values=Get-Variable -Name $Parameter.Name -Scope 1 -ea SilentlyContinue
if ( $Values -ne $Null)
{ $returnValue = AddToAll -Parameter $Parameter $returnValue $Values.Value }
else
{ $PSCmdlet.WriteWarning(\"The variable $($Parameter.Name) is not defined, processing the next parameter.\"«») }
}
New-Object PSObject -Property @{CommandName=$CommandName.Name;SetName=$Set.Name;Lines=$returnValue.Clone()}
}#foreach
}#process
} #New-TestSetParameters

[/code:1]
Un exemple :
[code:1]
#Récupère les métadonnées d'une commande
$cmd=Get-Command Test-Path
#Déclare un variable portant le même nom que le paramètre qu'on souhaite
#inclure dans le produit cartésien.
#Chaque valeur du tableau crée une ligne d'appel
$Path=@(
\"'c:\temp\unknow.zip'\",
\"'Test.zip'\",
\"(dir variable:OutputEncoding)\",
\"'A:\test.zip'\",
\"(Get-Item 'c:\temp')\",
\"(Get-Service Winmgmt)\",
'Wsman:\*.*',
'hklm:\SYSTEM\CurrentControlSet\services\Winmgmt'
)
#Le paramètre 'PathType' est une énumération
$PathType=@(\"'Container'\", \"'Leaf'\"«»)
#Génère les combinaisons du jeu de paramètre nommé 'Path'
#Les paramètres qui ne sont pas associés à une variable, génère un warning.
$result=New-TestSetParameters -command $Cmd -ParameterSetNames Path

#Nombre de lignes construites
$result.lines.count
#Exécution, Test-path n'a pas d'impact sur le FileSystem
$result.lines|% {Write-host $_ -fore green;$_}|Invoke-Expression
[/code:1]
Afin de générer un nouveau jeux de test, on peut supprimer les variables.
Ici dans ce cas la variable $Path devra être de nouveau définie, car obligatoire.
[code:1]
#'PathType','Path'|% {remove-variable $_ -ea SilentlyContinue}

#On ajoute le paramètre 'isValid' de type booléen
$isValid= @($true,$false)

#Génère les combinaisons du jeu de paramètre nommée 'Path'
#On supprime la génération du warning.
$result=New-TestSetParameters -command $Cmd -ParameterSetNames Path -WarningAction 'SilentlyContinue'
#Nombre de lignes construites
$result.lines.count
#Tri des chaines de caractères puis exécution
$Result.lines|Sort-Object|% {Write-host $_ -fore green;$_}|Invoke-Expression
[/code:1]
On peut aussi générer du code de test pour Pester ou un autre module de test :
[code:1]
$Template=@'
#
It \"Test ..TODO..\" {
try{
`$result = $_ -ea Stop
}catch{
Write-host \"Error : `$(`$_.Exception.Message)\" -ForegroundColor Yellow
`$result=`$false
}
`$result | should be (`$true)
}
'@
$Result.Lines| % { $ExecutionContext.InvokeCommand.ExpandString($Template) }
[/code:1]
Bon tests.
[edit]
Correction de la signature

Message édité par: Laurent Dardenne, à: 25/03/13 20:52

Message édité par: Laurent Dardenne, à: 9/06/13 11:55<br><br>Message édité par: Laurent Dardenne, à: 25/02/14 17:52

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 10 ans 10 mois #15107 par Laurent Dardenne
Correction du paramètre ParameterSetNames.
Celui doit être facultatif et avoir comme valeur par défaut '__AllParameterSets'.

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 10 ans 8 mois #15433 par Madrolle
Vous n'auriez pas un exemple concret d'utilisation, je ne comprends pas l'objet du script ... :(

ShellDealer sur Twitter

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 10 ans 8 mois #15435 par Laurent Dardenne
Perso écrit:

Vous n'auriez pas un exemple concret d'utilisation, je ne comprends pas l'objet du script ... :(

L'objectif est de créer des lignes d'appel d'une fonction à partir d'une liste de valeur et ce pour chaque paramètre.

On automatise ainsi le test des valeurs autorisées pour chaque paramètre.
On s'aperçoit à la longue que les paramètres de fonctions portent souvent sur les mêmes types d'informations : chemin, serveur, etc.
Au lieu de copier coller des valeur s de tests, autant utiliser PS pour les générer !

Pour l'exemple donné :
[code:1]
$result.lines|% {Write-host $_ -fore green;$_}|Invoke-Expression
[/code:1]
Le résultat est le suivant ( ligne d'appel suivi du résultat,ici un booléen ):
[code:1]
#Première boucle sur le paramètre -PathType avec la valeur 'Container'

Test-Path -Path 'c:\temp\unknow.zip' -PathType 'Container'
#False
Test-Path -Path 'Test.zip' -PathType 'Container'
#False
Test-Path -Path (dir variable:OutputEncoding) -PathType 'Container'
#False
Test-Path -Path 'A:\test.zip' -PathType 'Container'
#False
Test-Path -Path (Get-Item 'c:\temp') -PathType 'Container'
#True
Test-Path -Path (Get-Service Winmgmt) -PathType 'Container'
#False
Test-Path -Path Wsman:\*.* -PathType 'Container'
#False
Test-Path -Path hklm:\SYSTEM\CurrentControlSet\services\Winmgmt -PathType 'Container'
#True

#Seconde boucle sur le paramètre -PathType avec la valeur 'Leaf'

Test-Path -Path 'c:\temp\unknow.zip' -PathType 'Leaf'
#False
Test-Path -Path 'Test.zip' -PathType 'Leaf'
#False
Test-Path -Path (dir variable:OutputEncoding) -PathType 'Leaf'
#False
Test-Path -Path 'A:\test.zip' -PathType 'Leaf'
#False
Test-Path -Path (Get-Item 'c:\temp') -PathType 'Leaf'
#False
Test-Path -Path (Get-Service Winmgmt) -PathType 'Leaf'
#False
Test-Path -Path Wsman:\*.* -PathType 'Leaf'
#False
Test-Path -Path hklm:\SYSTEM\CurrentControlSet\services\Winmgmt -PathType 'Leaf'
#False
[/code:1]

L'idée était de construire 'une librairie de valeurs' associée à un attribut additionnel porté par un paramètre et couplé à cette fonction :
[code:1]
#Attribut de test fonctionnel, le code de l'attribut n'est pas livrée en production #&lt;%REMOVE%&gt;
[FunctionalType('FilePath')] #&lt;%REMOVE%&gt;
[string] $Chemin
[/code:1]
De cette manière on pouvait augmenter l'automatisme d'un cran, mais suite à un bug , cette approche nécessite de charger la dll de l'attribut additionnel obligatoirement au démarrage d'une session. A moins d'utiliser cet attribut uniquement sur un poste de dev configuré en ce sens...

S'il manque qq explications/infos n'hésite pas.<br><br>Message édité par: Laurent Dardenne, à: 28/07/13 14:07

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 10 ans 1 mois #17084 par Laurent Dardenne
Pour info, j'ai modifié la signature de la fonction New-TestSetParameters du premier post.

Voici deux fonctions permettant d'ajouter un niveau dans la génération du code :
[code:1]
function Get-CommonParameters{
[System.Management.Automation.Internal.CommonParameters].GetProperties()|
Foreach {$_.name}
}#Get-CommonParameters

function Get-ParameterSet {
#renvoi à partir de $Command
#les paramètres du jeux de paramètre indiqué
param (
[ValidateNotNull()]
[Parameter(Mandatory=$True,ValueFromPipeline=$True)]
[System.Management.Automation.CommandInfo] $Command,

[ValidateNotNullOrEmpty()]
[Parameter(Position=1,Mandatory=$false)]
[string]$ParameterSetName='__AllParameterSets',

[switch] $List

)
process {
if ( ($ParameterSetName -eq '__AllParameterSets') -and ($Command.ParameterSets.Count -gt 1) )
{ throw \&quot;La commande '$($Command.Name)' posséde plusieurs jeux de paramètre, précisez un des noms suivants : $($ofs=' , '; $cmd.ParameterSets|select -expand name)\&quot; }

$I=0
$Index=$Command.ParameterSets|? {$I++;$_.name -eq $ParameterSetName}|% { $I-1}
if ($i -eq -1 -or $index -eq $null)
{throw \&quot;Le jeu de paramètre nommé '$ParameterSetName' n'existe pas \&quot; }
Write-Debug \&quot;Index de $ParameterSetName : $Index\&quot;
if ($List)
{ $Command.ParameterSets[$Index].Parameters|Select -expand Name}
else
{ ,$Command.ParameterSets[$Index].Parameters }
}#process
} # Get-ParameterSet

function ConvertTo-VariableDeclaration {
#Associé à la fonction New-TestSetParameters
#Génére le code de déclaration des paramètres d'un jeux de paramètre d'une commande
[CmdletBinding()]
[OutputType([System.String])]
param (
[ValidateNotNullOrEmpty()]
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$True)]
$ParameterSet,

[ValidateNotNullOrEmpty()]
[Parameter(Position=1,Mandatory=$false)]
[scriptblock] $Code
)

begin {
[string[]]$CommonParameters=Get-CommonParameters
}
process {
$List=New-object System.Collections.Arraylist
Write-debug \&quot;Call ConvertTo-VariableDeclaration\&quot;
$ParameterSet|
Where { $CommonParameters -notContains $_.Name} |
foreach {
$TypeName=$_.Parametertype
$RuntimeType=$TypeName -as [type]
write-debug $RuntimeType.fullname
try{
$Type=[Activator]::CreateInstance($RuntimeType)
}catch {
$Type=$null
Write-Debug \&quot;Impossible de construire le type $($RuntimeType.fullname)\&quot;
}

write-debug ($Type -is [boolean])
write-debug ($Type -is [switch])
if ($Type -is [enum] )
{
$ofs=\&quot;','\&quot;
$List.Add( ('[string[]] ${0}=(''{1}'')' -f $_.Name,\&quot;$([enum]::GetValues($Type.GetType()))\&quot;«») ) &gt; $null
}
elseif ( ($Type -is [boolean]) -or ($Type -is [switch]))
{
$List.Add( ('${0}=@($true,$false)'-f $_.Name) ) &gt; $null
}
else
{
$List.Add( ('[{0}] ${1}={2}' -f $TypeName,$_.Name,'''\&quot;\&quot;''') ) &gt; $null
}
if ($PSBoundParameters.ContainsKey('Code'))
{ $List.Add(\&quot;$Code\&quot;«») &gt; $null }
}
,$List
}#process
}#ConvertTo-VariableDeclaration

&lt;#
$Cmd=Get-Command Test-Path
#insére l'appel de la génération du cas de test
#pour les variables déclarées au moment de l'appel
$BuildCall= { $Result +=(New-TestSetParameters -command $Cmd -ParameterSetNames 'Path' -Exclude 'Credential','UseTransaction').Lines

}
#Liste des paramètres communs
[string[]]$Commons=Get-CommonParameters

#Récupére la liste des paramètres du cmdlet ou de la fonction
#Excepté les paramètres communs
[string[]] $Parameters=$Cmd|Get-ParameterSet 'Path' -List|
Where { $Commons -notContains $_.Name}

#Supprime les variables de tests
Remove-Variable $Parameters -ea SilentlyContinue

#construit les lignes de déclaration des variables de test
#référencées par la fonction New-TestSetParameters
$CodeLines=$Cmd|
Get-ParameterSet 'Path'|
ConvertTo-VariableDeclaration -code $BuildCall

#Ajoute l'initialisation de la collection
$CodeLines.Insert(0,'$Result=@()') &gt; $null
$CodeLines.Insert(0,'$Cmd=Get-Command Test-Path') &gt; $null

#Crée le fichier de génération du jeux de test
$CodeLines &gt; C:\Temp\InitVarTest.ps1

#Reste à éditer ce fichier afin de renseigner le contenu des variables
# on peut également réordonner l'ordre de déclaration des variables
#notamment placer les paramètre de type switch et de type énumération
#au début des déclarations, les combinaisons seront améliorées
ii C:\Temp\InitVarTest.ps1

#Puis on éxécute la génération du jeux de test
. \&quot;C:\Temp\InitVarTest.ps1\&quot;

#Le résultat
$Result

#Enfin l'objectif : insérez le code généré dans un script de test
#&gt;
[/code:1]<br><br>Message édité par: Laurent Dardenne, à: 25/02/14 18:04

Tutoriels PowerShell
Pièces jointes :

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.086 secondes
Propulsé par Kunena