I am learning PowerShell, and created the following script cmdlet.

It reads one or more specified script files, to discover special functions (UnitTests, or functions with the Verb 'UnitTest' or 'DataTest'.

I designed the parameters to work like Get-Content, as it reads content from files.

I would like general feedback on this cmdlet (design, workings), but i am also intrested in correctness.

    Gets UnitTests from a UnitTestFile.
    UnitTests are PowerShell script functions, which are identified with the verb 'UnitTest' or 'DataTest'.
function Get-UnitTest {
    [CmdletBinding(DefaultParameterSetName = "Path")]
        <# Specifies a path to one or more UnitTestFiles. Wildcards are permitted. #>
        [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = $true)]
        [string[]] $Path,

        <# Specifies a LiteralPath to one or more UnitTestFiles. Wildcards are permitted. #>
        [Parameter(ParameterSetName = "LiteralPath", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]] $LiteralPath,

        <# Specifies the Verb to use to identify UnitTests. The default verbs are 'UnitTest' and 'DataTest'. This means that functions with a name like 'UnitTest-Get-ChildItem' or 'DataTest-Invoke-WebRequest' are found. #>
        [String[]] $Verb = @( "UnitTest", "DataTest" )

    Process {

        foreach($curPath in ($LiteralPath + $Path | Where {$_} | Resolve-Path)) {
            if(Test-Path $curPath -PathType Leaf) {

                Write-Trace "Getting UnitTests from '$curPath'."
                . $curPath

                # find and return all unit tests (from the requested file)
                Get-Command -Verb $Verb `
                    | Where { $_.CommandType -eq "Function" } `
                    | Where { ( $_.ScriptBlock.File ) -eq $curPath }

This cmdlet can be called as follows:

Get-UnitTest -Path .\somefile.ps1
Get-UnitTest -Path .\somefile.ps1 -Verb 'OtherTest'
Get-ChildItem -Recurse *.ps1 | Get-UnitTest

1 Answer 1


Looks good. Personally, I would take:

foreach($curPath in ($LiteralPath + $Path | Where {$_} | Resolve-Path)) {

and break out the collection generation code. Specifically into something like:

$PathList = Join-Path $literalPath $Path | Resolve-Path 

You shouldn't need to worry about a Where-Object to validate that it isn't $null. If there is no result it won't go up the pipeline.

If you Dot Include all of your Scripts you can wait until all are included then check your commands. This will run anything else you have in the script though.

It may be safer to use the parser like this sample below. In this code I looked for catch blocks to insert a breakpoint , but you could have it filter for your test prefixes too.

$ParseError = $null
$Tokens = $null
$AST = [System.Management.Automation.Language.Parser]::ParseInput($psISE.CurrentFile.Editor.Text, [ref]$Tokens, [ref]$ParseError)
if($ParseError) { 
    foreach($er in $ParseError) { Write-Error $er }
    throw "There was an error parsing script (See above). We cannot expand aliases until the script parses without errors."
$a = $ast.FindAll({($args[0] -is [System.Management.Automation.Language.CatchClauseAst])},$true)
  • Josh

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.