Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use values that look like PowerShell ScriptBlocks in Gherkin table cells #1114

Closed
fourpastmidnight opened this issue Oct 10, 2018 · 1 comment

Comments

@fourpastmidnight
Copy link
Contributor

General Description

I'm trying to test a PowerShell script that I wrote which accepts at least one parameter of type [object]. Within the script, I try to determine if the parameter value is a script block, and if so, execute the script block to retrieve the value returned by the block (if any).

Assume a trivial script such as:

filter My-Script {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True, Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        $Argument
    )

    Process {
        try {
            if ($Argument -is [ScriptBlock]) {
                Invoke-Command -ScriptBlock $Argument
            } else {
                $Argument
            }
        } catch {
            $PSCmdlet.ThrowTerminatingError($PSItem)
        }
    }
}

In order to test that the script can properly handle all kinds of parameters, as well as other parameter types, I used a Gherkin Scenario Outline and a table of examples/scenarios. Here's a small snippet of what my Gherkin feature file might look like:

Feature: My-Script

    As a PowerShell script and module author,
    in order to test that my script can accept parameters of various types
    I want to pass various argument types for the script parameters.

Scenario Outline: Can pass various argument types to script parameters

    Given I have an argument expression '<ArgumentExpression>'
     When I invoke the script with the argument expression
     Then the result should be '<ExpectedResult>'

Scenarios:
    | ArgumentExpression      | ExpectedResult      |
    | $null                   | $null               |
    | ""                      | ""                  | # This doesn't work as you would expect either, but you can "make it work"...
    | 0                       | 0                   |
    | $true                   | $true               |
    | { `$null }              | $null               | # If I don't use the '`', this doesn't work
    | { "this doesn't work" } | "this doesn't work" | # This doesn't work no matter what I do
Given "I have an argument expression '(?<ArgumentExpression>.*)'" {
    Param([object]$ArgumentExpression)
    switch -Regex ($ArgumentExpression) {
        "^`$null$"                      { $argument = $null }
        "^`{\s*(?<ScriptBody>.*)\s*`}$" { $argument = [ScriptBlock]::Create($Matches.ScriptBody) }
        "^(?<StringValue>`".*`")$"      { $argument = $Matches.StringValue -as [string] } # This makes the "" in the table work
        "^(?i:(?<Boolean>true|false))$" { $argument = [Boolean]::Parse($Matches.Boolean) }
        "^\d+$"                         { $argument = $ArgumentExpression -as [int] }
        default                         { $argument = $ArgumentExpression }
    }

    $argument
}

When "I invoke the script with the argument expression" {
    Write-Host "About to call My-Script with:"
    Write-Host "`$argument = '$argument' ($(if ($null -ne $argument) { $argument.GetType().FullName } else { "`$null" }))"
    $actual = My-Script -Argument = $argument
}

Then "the result should be '(?<ExpectedResult>.*)'" {
    Param($ExpectedResult)
    $actual | Should -Be $ExpectedResult
}

If you attempt to run this scenario, all values work as expected (though some with some "hacks" as noted) except for the last row in the table.

    [+] Given I have an argument expression '{ this doesn't work }'"
About to call My-Script with:
$argument = '"this doesn't work"  "this doesn't work"' (System.Object[])
    [+] When I invoke the script with the argument expression
    [-] Then the result should be 'this doesn't work'.
       Expected 'this doesn't work', but got @({"this doesn't work" }, '"this doesn't work"').
        xx:      $actual | Should -Be $ExpectedResult

Environment

I'm running Windows 10 1803 Enterprise with PowerShell 5.1 and Pester 4.2.0 through the integrated PowerShell terminal of VS Code Insiders. It should be noted that the tests fail in the same way at the standard PowerShell console.

Pester version     : 4.4.2 C:\Program Files\WindowsPowerShell\Modules\Pester\4.4.2\Pester.psd1
PowerShell version : 5.1.17134.165
OS version         : Microsoft Windows NT 10.0.17134.0

Expected Behavior

I expect that when Pester parses the table values that the values should all be treated as strings, much like in SpecFlow, for example. In this way, I could pass a value such as { "this should work" } and generate a script block from it:

"{ "this should work }" -match "`{\s*(?<ScriptBody>.*)\s*`}"
$argument = [ScriptBlock]::Create($Matches.ScriptBody)
$argument

Current Behavior

Currently, it appears there's some "pre-processing" of values as they are retrieved from Gherkin scenario tables--either they are purposfully pre-processed/eval'ed or they are not properly escaped so that the values remain as strings once parsed. This causes issues with being able to test some aspects of scripts.

Possible Solution

It depends on how table cells are being parsed currently. I've looked through the Gherkin.psm1 file. If I have some time, I'll try to do a deep dive through it--because I can't see anything obviously wrong, so it's probably a bit tricky (for me, anyway).

@fourpastmidnight
Copy link
Contributor Author

Looks like I had something wrong in the switch statement that was attempting to match the script block. This does work as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant