Verify Invoke-WebRequest statuscode 404 with Pester

A 404 result coming from an Invoke-WebRequest is usually a bad thing. This time, however, the 404 is a result I actually hoped to see. I am creating a custom Azure Devops extension that talks with the API of VMWare VRealize 8. The extension allows users to create a new resources such as a VM within VRA through an Azure Devops Pipeline. The API that I am querying generates a 200 statuscode if the resource with a curtain name exists. If this resource does not exists, a 404 is thrown. I wrote a function that checks 3 things.

  1. Is the statuscode 200? Throw an error.
  2. Is the statuscode 404? The test passed and we do not really have to do anything.
  3. The statuscode is neither 200 or 404? It seems like something else is up, so throw an error with some bonus information.
Function Get-vraDeploymentName {
    Param ($Uri, $Name, $Headers)
    $RequestURL = "$Uri/deployment/api/deployments/names/$Name"

    #Get the statuscode.
    $Result = try { 
        (Invoke-WebRequest -Uri $RequestURL -Method GET -Headers $Headers)
    }
    catch [System.Net.WebException] { 
        $_.Exception.Response 
    }

    $statusCodeInt = [int]$Result.StatusCode
    
    #200 is bad. 404 is good. Everything else is very bad
    If ($statusCodeInt -eq 200) {
        Throw "A resource with the name `'$Name`' already exists."
    } 
    ElseIf ($statusCodeInt -eq 404) {
        Write-Host "The name `'$Name`' is available for creating the new resource."
    } 
    Else {
        throw "Module = Get-vraDeploymentName: $($_.Exception.Message)"
    }
}

How do we test such functionality with Pester? You can mock the Invoke-WebRequest with some JSON output, but this will prevent Pester from testing the try/catch loop. I decided to mock the object ‘System.Net.HttpWebResponse’.

Mock Invoke-WebRequest {
    $status = [System.Net.WebExceptionStatus]::ConnectionClosed
    $response = New-MockObject -type 'System.Net.HttpWebResponse'
    $response | Add-Member -MemberType noteProperty -Name 'StatusCode' -Value 404 -force
    $exception = New-Object System.Net.WebException "Fout" , $null, $status, $response
        
    Throw $exception
}

So what is actually happening here?
When running Invoke-WebRequest, PowerShell uses the System.Net assemblies to establish the connection with the API. The result of this action is faked with Mock-Object. Powershell believes that the connection actually failed and it returns the results we need for the test.

Here is the full set of Pester tests I wrote for this particular function.

Describe "Get-vraDeploymentName" {
    It "Resource exist" {
        Mock Invoke-WebRequest { @{ StatusCode = 200 } }
        { Get-vraDeploymentName -Name 'test' -Uri 'http://fakeurl' } | Should -Throw "A resource with the name `'test`' already exists."
    }
    It "Resource not exist" {
        Mock Invoke-WebRequest {
            $status = [System.Net.WebExceptionStatus]::ConnectionClosed
            $response = New-MockObject -type 'System.Net.HttpWebResponse'
            $response | Add-Member -MemberType noteProperty -Name 'StatusCode' -Value 404 -force
            $exception = New-Object System.Net.WebException "Fout" , $null, $status, $response
        
            Throw $exception
        }
        Mock Write-Host {}
        Get-vraDeploymentName -Name 'test' -Uri 'http://fakeurl'
        Assert-MockCalled Write-Host -Exactly 1 -ParameterFilter { $Object -eq "The name `'test`' is available for creating the new resource." }
    }
    It "Failed" {
        Mock Invoke-WebRequest { }
        { Get-vraDeploymentName -Name 'test' -Uri 'http://fakeurl' } | Should -Throw "Module = Get-vraDeploymentName: "
    }
}