Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 816
craig Avatar asked Oct 12 '25 13:10

craig


2 Answers

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)

like image 121
mclayton Avatar answered Oct 14 '25 16:10

mclayton


On PowerShell Core this doesn't work for me:

[System.Net.HttpWebResponse].GetField('m_StatusCode', 'NonPublic, Instance')

And thats why your Mock isn't returning what you expect. That line does work on Windows PowerShell however. Not sure what the right equivalent is on PSCore, needs research but thought i'd get you this far in the meantime.

like image 22
Mark Wragg Avatar answered Oct 14 '25 18:10

Mark Wragg



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!