Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Subscribe
Archives
July 11, 2024

Pester Testing .NET with Copilot Part 3

I've been sharing my experiences writing Pester tests for a PowerShell function that uses the .NET Framework instead of invoking native cmdlets. The process has also involved using GitHub Copilot in VS Code. As you've seen this has been hit and miss. While there were times when Copilot accelerated my scripting, there were times, I needed to step in. When we left off, I had refactored the function to use private functions that wrapped around the .NET method invocations. You can only Pester test or mock a PowerShell command like a function or cmdlet. This is where the function stands now after the refactoring.

Function Get-OSDetail {

<#

.Synopsis

Get operating system details via CIM

.Description

Use this command to query one or more remote computers using CIM to get operating

system details.

.Example

PS C:\> Get-OSDetail

Name                   : Microsoft Windows 11 Pro

Version                : 10.0.22635

Build                  : 22635

OSArchitecture         : 64-bit

RegisteredUser         : Jeff

RegisteredOrganization :

InstallDate            : 5/17/2022 2:54:52 PM

ComputerName           : THINKX1-JH

.Link

Get-CimInstance

#>

    [CmdletBinding()]

    [OutputType('OSDetail')]

    Param(

        [Parameter(

            Position = 0,

            ValueFromPipeline

        )]

        [Alias('CN', 'Server')]

        [ValidateNotNullOrEmpty()]

        [Microsoft.Management.Infrastructure.CimSession[]]$CimSession = $Env:ComputerName

    )

    Begin {

        Write-Verbose "Starting $($MyInvocation.MyCommand)"

        #define the WQL query

        $Query = 'Select CSName,Caption,Version,BuildNumber,InstallDate,OSArchitecture,RegisteredUser,Organization from Win32_OperatingSystem'

        #initialize reference variables

        New-Variable -Name ci

        New-Variable -Name ce

        #define private helper functions that I can mock in my pester tests

        function TestCimConnection {

            param(

                [Microsoft.Management.Infrastructure.CimSession]$CimSession

            )

            $cs.TestConnection([ref]$ci, [ref]$ce)

        } #TestCimConnection

        function InvokeQuery {

            param(

                [Microsoft.Management.Infrastructure.CimSession]$CimSession,

                [string]$Query

            )

            $CimSession.QueryInstances('Root/Cimv2', 'WQL', $Query)

        }

    } #begin

    Process {

        foreach ($cs in $CimSession) {

            #capture connection failures to a variable

            Write-Verbose "Testing connection to $($cs.ComputerName.ToUpper())"

            If (TestCimConnection -CimSession $cs) {

                Write-Verbose "Querying $($cs.ComputerName.ToUpper())"

                $data = InvokeQuery -CimSession $cs -Query $Query

                [PSCustomObject]@{

                    PSTypeName             = 'OSDetail'

                    Name                   = $data.Caption

                    Version                = $data.Version -as [Version]

                    Build                  = $data.BuildNumber -as [Int32]

                    OSArchitecture         = $data.OSArchitecture

                    RegisteredUser         = $data.RegisteredUser

                    RegisteredOrganization = $data.Organization

                    InstallDate            = $data.InstallDate

                    ComputerName           = $data.CSName

                }

            }

            else {

                Write-Warning "Unable to connect to $($cs.ComputerName.ToUpper()). $($ce.Message)"

            }

        } #foreach

    } #process

    End {

        Write-Verbose "Ending $($MyInvocation.MyCommand)"

    } #end

} #end function Get-OSDetail
I want to mock the private functions because the Pester test isn't supposed to run the actual function. It only needs to test the function's logic and my requirements. However, as I alluded to at the end of the previous article, there is going to be a problem. I can write a mock of the private function like this:
Mock TestCimConnection { $true }  -Verifiable -ParameterFilter { $CimSession.ComputerName -eq 'SRV1' }
But when I run the Pester test, I get an error: `CommandNotFoundException: Could not find Command TestCimConnection`. This is because Pester is looking for a public function called `TestCimConnection`. It doesn't know about the private function. How can I work around this problem?
Get a premium subscription for full article and archive access

I've been sharing my experiences writing Pester tests for a PowerShell function that uses the .NET Framework instead of invoking native cmdlets. The process has also involved using GitHub Copilot in VS Code. As you've seen this has been hit and miss. While there were times when Copilot accelerated my scripting, there were times, I needed to step in.

When we left off, I had refactored the function to use private functions that wrapped around the .NET method invocations. You can only Pester test or mock a PowerShell command like a function or cmdlet.

This is where the function stands now after the refactoring.

Want to read the full issue?
GitHub Bluesky LinkedIn About Jeff
Powered by Buttondown, the easiest way to start and grow your newsletter.