Archive

Archive for the ‘Anouncements’ Category

PowerShell TDD: Testing ShouldProcess

July 12th, 2022 No comments

The built-in PowerShell feature ShouldProcess is considered best practice for commands that perform destructive operations.

function Remove-Something {
    [CmdletBinding(SupportsShouldProcess)]
    param()
    if ($PSCmdlet.ShouldProcess("Something", "Permanently remove")) {
        # Code that performs the destructive operation
    }
}

The problem with ShouldProcess is that it may prompt the user for manual confirmation, which makes automated testing difficult or impractical. The normal approach to automate testing of processes that involves manual prompting is to mock the part which performs it. However, since ShouldProcess is a .NET method on an automatic context variable ($PSCmdlet), testing frameworks like Pester aren’t able to replace its implementation with a mock.

Fortunately, there is a way to make ShouldProcess adhering commands fully testable. It just requires a little bit of extra work, such as wrapping ShouldProcess into a PowerShell function of its own.

Wrapping ShouldProcess

Here’s a PowerShell function that wraps the ShouldProcess method and makes it possible to use mocks to bypass the prompting.

function Invoke-ShouldProcess {
    # Suppress the PSShouldProcess warning since we're wrapping ShouldProcess to be able to mock it from a Pester test.
    # Info on suppressing PSScriptAnalyzer rules can be found here:
    # https://github.com/PowerShell/PSScriptAnalyzer/blob/master/README.md#suppressing-rules
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.Management.Automation.PSCmdlet]
        $Context,
        [Parameter(Mandatory)]
        [string]
        $Target,
        [Parameter(Mandatory)]
        [string]
        $Operation,
        [string]
        $Message
    )
    if ($Message) {
        $Context.ShouldProcess($Message, $Target, $Operation)
    } else {
        $Context.ShouldProcess($Target, $Operation)
    }
}

Actually, there are four overloads of the ShouldProcess method, but the code above only handles the two most useful ones (in my opinion).

The next step is to replace the ShouldProcess method in the command, and use the wrapper instead.

function Remove-Something {
    [CmdletBinding(SupportsShouldProcess)]
    param()
    if (Invoke-ShouldProcess -Context $PSCmdlet -Target "Something" -Operation "Permanently remove") {
        # Code that performs the destructive operation
    }
}

Note that you need to provide the command’s $PSCmdlet variable as the context on which ShouldProcess should be invoked.

Mocking Invoke-ShouldProcess

Now our command is ready for some Pester testing. Here are a couple of test stubs that show you how to mock the wrapper. Hopefully, you’ll be able to take it from here and start testing your own ShouldProcess-aware commands.

It 'Should perform the operation if the user confirms' {
    Mock Invoke-TDDShouldProcess { $true }

    Remove-Something
        
    # Code that verifies that the operation was executed
}

It 'Should not perform the operation if the user aborts' {
    Mock Invoke-TDDShouldProcess { $false }

    Remove-Something

    # Code that verifies that the operation was not executed
}

Introducing TDDSeams

I have created a PowerShell module named TDDSeams which has wrappers for ShouldProcess and its sister ShouldContinue, as well as a couple of helper methods that implement the best practice behavior described by Kevin Marquette in his excellent post Everything you wanted to know about ShouldProcess.

You can install the TDDSeams module from an elevated PowerShell with the following command.

Install-Module TDDSeams

Also, if you are interested in test-driven PowerShell development, check out my previous post PowerShell TDD: Testing CmdletBinding and OutputType.

PowerShell TDD: Testing CmdletBinding and OutputType

July 11th, 2022 No comments

A while ago, I decided to add PowerShell to my automation toolbox. Since I believe the best way to learn a new language is to do test-driven development and PowerShell has a fantastic module for this, called Pester. The framework is super intuitive, easy to do mocking in, and allows you develop your code design from tests.

However, one thing bugged me. I didn’t seem to be able to write code that tested whether a function had declared CmdletBinding (i.e. was an Advanced Function), or if it had declared OutputType.

# How can I test this?
function MyFunction {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    [OutputType('String')]
    ...
}

Google didn’t have anything useful on this subject so I tried my luck on StackOverflow and mclayton pointed me in the right direction (although, as mclayton puts it: it’s a bit of a mouthful). It turns out that I can use the built-in Abstract Syntax Tree (AST) and specifically the Param block attributes to find out if CmdletBinding is declared.

Testing CmdletBinding

The below function takes a command as input and looks for a CmdletBinding param block attribute.

function Test-CmdletBinding {
[OutputType([Bool])]
[CmdletBinding()]
    param (
        # Parameter help description
        [Parameter(Mandatory)]
        [System.Management.Automation.CommandInfo]
        $Command
    )

    $attribute = $command.ScriptBlock.Ast.Body.ParamBlock.Attributes | where-object { $_.TypeName.FullName -eq 'CmdletBinding' };

    $null -ne $attribute
}

You can then use the helper function in a Pester test, like this.

It "Should be an advanced function" {
    $c = Get-Command -Name MyCommand
    Test-CmdletBinding $c | Should -BeTrue
}

Testing CmdletBinding Arguments

That’s great, but what about arguments, like SupportsShouldProcess or ConfirmImpact?

[CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]

How can I test for those? Well, that’s where we get mouthful bits I guess, but the good news is that it’s doable. Here’s a helper function that can test those scenarios. It takes a command, an argument name, and an optional argument value and returns true if the command meets those conditions.

function Test-CmdletBindingArgument {
    [CmdletBinding()]
    [OutputType([Bool])]
    param (
        # Parameter help description
        [Parameter(Mandatory)]
        [System.Management.Automation.CommandInfo]
        $Command,
        [Parameter(Mandatory)]
        [string]
        $ArgumentName,
        [Parameter()]
        [string]
        $ArgumentValue
    )

    $attribute = $command.ScriptBlock.Ast.Body.ParamBlock.Attributes | where-object { $_.TypeName.FullName -eq 'CmdletBinding' };

    if ($attribute) {
        $argument = $attribute.NamedArguments | where-object { $_.ArgumentName -eq $ArgumentName };

        if ($null -eq $argument) {
            # The attribute does not have the argument, return false
            $false
        } elseif ($argument.ExpressionOmitted) {
            $ArgumentValue -eq '' -or $ArgumentValue -eq $true
        } elseif ($argument.Argument.Extent.Text -eq '$true') {
            $ArgumentValue -eq '' -or $ArgumentValue -eq $true
        } elseif ($argument.Argument.Extent.Text -eq '$false') {
            $ArgumentValue -eq $false
        } else {
            $ArgumentValue -eq $argument.Argument.Value
        }
    } else {
        # No such attribute exists on the command, return false
        $false
    }
}

The code handles both implicit and explicit values, e.g.

[CmdletBinding(SomeAttribute)]
[CmdletBinding(SomeAttribute=$true)] # same as having the attribute declared
[CmdletBinding(SomeAttribute=$false)] # same as not having the attribute declared
[CmdletBinding(SomeAttribute='Some Value')]

Here are a couple of examples of Pester tests using the helper function.

It "Should have CmdletBinding with ConfirmImpact set to High" {
    $c = Get-Command -Name MyCommand
    Test-CmdletBindingArgument $c -ArgumentName 'ConfirmImpact' -ArgumentValue 'High' | Should -BeTrue
}

It "Should have SupportShouldProcess declared" {
    $c = Get-Command -Name MyCommand
    Test-CmdletBindingArgument $c -ArgumentName 'SupportShouldProcess' | Should -BeTrue
}

Testing OutputType

Testing OutputType requires a slightly different approach. Since the OutputType attribute declares a type we have to access it through the positional arguments (instead of named arguments that were used for CmdletBinding).

Here’s a helper function to verify that a given command has declared a given type as its output type.

function Test-OutputType {
    [OutputType([Bool])]
    [CmdletBinding()]
    param (
        # Parameter help description
        [Parameter(Mandatory)]
        [System.Management.Automation.CommandInfo]
        $Command,
        [Parameter(Mandatory)]
        [string]
        $TypeName
    )

    $attribute = $command.ScriptBlock.Ast.Body.ParamBlock.Attributes | where-object { $_.TypeName.FullName -eq 'OutputType' };

    if ($attribute) {
        $argument = $attribute.PositionalArguments | where-object {
            if ($_.StaticType.Name -eq 'String') {
                $_.Value -eq $TypeName 
            } elseif ($_.StaticType.Name -eq 'Type') {
                $_.TypeName.Name -eq $TypeName 
            } else {
                $false
            }
        }
        if ($argument) {
            $true
        } else {
            $false
        }
    } else {
        $false
    }
}

Note that a type can be declared as a string or as a type, but the helper function handles both cases:

[OutputType([Bool])]
[OutputType('System.Bool')]

And here’s an example of how it can be used within a Pester test.

It "Should have Output type Bool" {
    $c = Get-Command -Name MyCommand
    Test-TDDOutputType $c -TypeName 'Bool' | Should -BeTrue
}

Introducing TDDUtils

I have created a PowerShell module called TDDUtils, which contains the above functions (although named with a TDD prefix) as well as more general versions that allows you to test other attributes than CmdletBinding and OutputType.

 You can install it from an Administrator PowerShell with the below command.

Install-Module TDDUtils

I plan to add more useful functions to that module as I go on with my PowerShell TDD journey, and if you want to contribute or just give me some suggestions, feel free to contact me on Twitter.

iOS 6 Recipes Is Out

January 11th, 2013 7 comments

Allright!

I’ve had this dream, for like ages, to become a published book author. Now I’ve done it. iOS 6 Recipes is out. (Actually, it’s been out since late november but I’ve been so exhausted that I haven’t been able to muster the energy to blog about it.)

iOS 6 Recipes is based on another book, iOS 5 Recipes, by Colin Francis and Shawn Grimes. My job was to upgrade that book with the new features of iOS 6, but as it turned out I had to rewrite most of the original content, keeping only the general structure and most of the old topics. I’m very proud of the result and although there are many ways in which it could still be improved, I believe I’ve accomplished the goal of producing a better book, valuable to its readers.

But I couldn’t have pulled this off if it wasn’t for the fantastic team at Apress, especially Anamika Panchoo (Coordinating Editor), Anselm Bradford (Technical Rewiever), Douglas Pundick (Developmental Editor) and Linda Seifert (Copy Editor).

If you’ve read iOS 6 Recipes and want to share your thoughts, or ask me questions, feel free to contact me, either through the comments here or by emailing me (you’ll find my email address in the book).

Cheers!

Categories: Anouncements, books Tags: , ,

Incremental Development and Regression Testing Paper Released

May 17th, 2011 No comments

As you might know I wrote about the regression testing problem in The Swamp of Regression Testing. One of the commenters of that post, Ricky Clarkson, thought that the extensive use of the agile word got in the way of the underlying message.

I’d love to see a copy of this article without “if you don’t do X, you won’t be agile”, because where I work, agile isn’t a goal, but quality and quick turnaround is. If I passed this around, the word ‘agile’ would get in the way of an otherwise great message.

That made so much sense that I decided to do just that. I wrote a new version, a complete rework of the original post, and published it as a separate document so that it can be easily passed around.

You find it under Shared Stuff on my website, or by following the below link.

Incremental Development and Regression Testing (PDF)

Please don’t hesitate to contact me if you have any feedback to share.

Cheers!

Categories: Anouncements, software development Tags:

I Have a New Employer

October 29th, 2010 No comments

When I was young…er a friend of mine barged into a role playing session (Yes I was a nerd. Am.) with the following question: “Do you notice anything different about me?” He had his newly acquired driver’s license taped to his forehead…

I would do something similar now to mark an equally great change in my life, but  I haven’t figured out how to make my newly acquired employment stick to my forehead. So, here it is instead, in plain text:

I have gotten myself into consulting.

My new employer is Know IT, a fast growing Swedish IT company mainly targeting the Scandinavian IT sector.

I’m quite happy with the change. Although I enjoyed the seven years with my previous employer very much, I hope the new position will bring me diversity and professional growth.

If you’re thinking about changing your own work situation, but haven’t found the guts to do so, then I can recommend reading the inspiring post by Justin Etheredge. He, obviously a couple years ahead of me, quit consulting and started his own company. I can totally relate to that. But one step at a time, right?

Cheers!

Categories: Anouncements Tags: