Search code examples
powershellpowershell-corepester

Having difficultly mocking Invoke-WebRequest's BasicHtmlWebResponseObject


I'm attempting to test this section of a PowerShell function:

# post
$Response = Invoke-WebRequest -Method POST -Uri $Uri -Body $Body -ContentType 'application/xml'

# parse Response.Content; return as System.Xml.XmlDocument
[xml]$Response.Content

by mocking the BasicHtmlWebResponseObject that is returned by Invoke-WebRequest:

Mock Invoke-WebRequest { 

    $WebResponse = [System.Net.HttpWebResponse]::new()
    [System.Net.HttpWebResponse].GetField('m_StatusCode', 'NonPublic, Instance').SetValue(
        $WebResponse,
        200,
        'NonPublic,SetField',
        $null,
        (Get-Culture)
    )

    $Content = '<?xml version="1.0" encoding="UTF-8"?><response><control>failure<status></status></control><operation><result><status>failure</status></result></operation></response>'
    $Response = [Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject]::new($WebResponse,$Content)
    return $Response
}

This assertion fails because I'm not creating the HttpWebResponse or BasicHtmlWebResponseObject correctly:

It "returns the response's Content object" {
    # act
    $Content = Send-Request -Session $Session

    # assert
    Assert-MockCalled Invoke-WebRequest
    $Content | Should -BeOfType [xml]
    $Content.response.control.status | Should -Be 'success'
    $Content.response.operation.result.status | Should -Be 'success'
}

** edit **

I thought about using New-MockObject:

Mock Invoke-WebRequest { 
    $Response = New‐MockObject -Type Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject
    $Response.Content = '<?xml version="1.0" encoding="...'
}

but, the Content property is read-only.

** /edit **

What am I missing?


Solution

  • A slightly simpler alternative might be to wrap your invoke-webrequest in a function and just mock that instead. E.g.

    function Get-XmlFromUri
    {
        param( $Uri, $Method, $Body )
         $Response = Invoke-WebRequest -Method $Method -Uri $Uri -Body $Body -ContentType 'application/xml’
        [xml]$Response.Content
    }
    

    Now you can mock Get-XmlFromUri and just return a System.Xml.XmlDocument object from hard-coded xml, which is much easier to create than a BasicHtmlWebResponseObject that needs reflection calls spin up.

    Mock Get-XmlFromUri { 
        [xml] '<?xml version="1.0" encoding="UTF-8"?>
        <response>
            <control><status>success</status></control>
            <operation><result><status>success</status></result></operation>
        </response>'
    }
    

    Or, depending on how much like BasicHtmlWebResponseObject your code needs it to be, you can just return a hashtable from your invoke-webrequest mock that has the properties you need:

    Mock Invoke-WebRequest { 
        new-object pscustomobject -property @{
            Content = '<?xml version="1.0" encoding="UTF-8"?>
        <response>
            <control><status>success</status></control>
            <operation><result><status>success</status></result></operation>
        </response>’
        }
    }
    

    (apologies for code formatting - currently typing one handed on an iPhone at 4 AM holding a not-very-sleepy baby :-S)