Mocking with Pester

This topic contains 2 replies, has 2 voices, and was last updated by  Michael 2 months, 2 weeks ago.

  • Author
    Posts
  • #76505

    Michael
    Participant

    Hi,

    I am trying to write a unit Pester test and not sure why I cannot get this to work. I am trying to verify that each code path works correctly. My function is below along with my Pester test. Please help! My function is calling an exposed Web Endpoint. I have tried different ways to mock out the responses but the common error I am getting is "You cannot call a method on a null-valued expression' which is pointing to the $obj variable. I understand the error but I thought it would be bypassed since I am mocking the New-Object cmdlet.

    Function

    function Test-Endpoint {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory=$true,
                        ValueFromPipeline=$true,
                        HelpMessage="Write anything to echo")]
            [string]$Message
        )
        Begin {
            $uris = 'x','y','z'
            Write-Verbose "URI's to test: $uris"
        }
        Process {
            Write-Verbose "Beginning Process block"
            foreach ($uri in $uris) {
                Try{
                    $uri = "http://$uri/Something.svc/echo/$message"
                    $request = Invoke-WebRequest   -Uri "$uri" `
                                        -Method 'Get' `
                                        -ContentType 'application/json' `
                                        -UseBasicParsing `
                                        -ErrorAction Stop
                } Catch {
                    Write-Warning "$uri failed to return echo message"
                    Write-Warning "$_.Exception.Message"
                }
                if($?) {
                    Write-Verbose "Test $uri echo complete"
                    $props = @{ 'URI' = $uri;
                                'StatusCode' = $request.StatusCode;
                                'Status' = $request.StatusDescription;
                                'Content' = $request.Content}
                    $obj = New-object -TypeName PSObject -Property $props
                    $obj.PSObject.TypeNames.Insert(0,'xyz.SystemInfo')
                    Write-Output $obj
                }
            }
        }
        End {}
    }
    

    Pester Unit Test

    Import-Module SomeModule
    
    Describe "Test-Endpoint" {
        InModuleScope SomeModule {
            it 'attempts to return echo message if Invoke is successful' {
                mock -CommandName Invoke-WebRequest -MockWith {
                    $request = @{'URI' = 'someURI';
                                'Content' = 'someContent';
                                'Status' = 'someStatus';
                                'StatusCode' = 'someStatusCode'
                                }
                    return $request
                }
                mock -CommandName New-Object -MockWith {
                    $obj = $result
                    return $($obj.typeNames.Insert(0,'xyz.SystemInfo'))
                }
                Test-Endpoint -Message "SomeMessage"
                Assert-MockCalled -CommandName 'Invoke-WebRequest' -Times 1 -Scope It
                Assert-MockCalled -CommandName 'New-Object' -Times 1 -Scope It
            }
            it 'returns error message if invoke to endpoint fails' {
                mock -CommandName Invoke-WebRequest -MockWith {
                        return $Error
                }
                mock -CommandName 'New-Object' -MockWith { 
                    return $null
                }
                Test-CorpSysBillingSummaryEcho -Message "someMessage"
                Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It
                Assert-MockCalled New-Object -Times 0 -Scope It
            }   
        }    
    }
    
  • #76507

    Adam Bertram
    Moderator

    You are mocking New-Object with the exact same code that's actually running. I'd remove that mock and test that the function returns the expected object.

  • #76510

    Michael
    Participant

    Hi,

    3rd editing my response here. Sorry. What do you think about this? Is there a better way to do this? Any advice? I still want to create another Context block for when the codepath goes to Error. Not sure how to do that yet.

    Import-Module SomeModule
    
    Describe "Test-Endpoint" {
        InModuleScope SomeModule {
            Context 'Verifying success code path of echo method and expected fields are present & not null' {
                it 'attempts to invoke web api code path' {
                    mock -CommandName Invoke-WebRequest -MockWith {
                        $request = @{'URI' = 'someURI';
                                    'Content' = 'someContent';
                                    'Status' = 'someStatus';
                                    'StatusCode' = 'someStatusCode'
                                    }
                        return $request
                    }
                    $global:result = Test-Endpoint -Message "SomeMessage"
                    Assert-MockCalled -CommandName 'Invoke-WebRequest' -Times 1 -Scope It
                }
                it 'URI field exists and not null' {
                    $result.URI.ToString() | Should Not Be $null
                }
                it 'Content field exists and not null' {
                    $result.Content.ToString() | Should Not Be $null
                }
                it 'Status field exists and not null' {
                    $result.Status.ToString() | Should Not Be $null
                }
                it 'StatusCode field exists and not null' {
                    $result.StatusCode.ToString() | Should Not Be $null
                }
                it '4 NoteProperties exist' {
                    ($result | Get-Member -MemberType NoteProperty).length | Should Be 4
                }
            }
        }
    }
    

You must be logged in to reply to this topic.