3

是否有任何简单的方法可以在 Pester 中模拟 Rest API 调用

这是我的代码,我只需要在 Pester 中模拟那些 Rest API 调用并对其进行测试,有人可以在这里帮助我吗?

Describe 'Test worldclockapi.com' {
    BeforeAll {
        $response = Invoke-WebRequest -Method 'GET' -Uri 'http://worldclockapi.com/api/json/utc/now'
        $responseContent = $response.Content | ConvertFrom-Json
        Write-Output $responseContent
    }

    It 'It should respond with 200' {
        $response.StatusCode | Should -Be 200
    }
    
    It 'It should have a null service response' {
        $responseContent.serviceResponse | Should -BeNullOrEmpty
    } 
    
    It 'It should be the right day of the week' {
        $dayOfWeek = (Get-Date).DayOfWeek
        $responseContent.dayOfTheWeek | Should -Be $dayOfWeek
    }
    
    It 'It should be the right year' {
        $year = Get-Date -Format 'yyyy'
        $responseContent.currentDateTime | Should -BeLike "*$year*"
    }
    
    It 'It should be the right month' {
        $month = Get-Date -Format 'MM'
        $responseContent.currentDateTime | Should -BeLike "*$month*"
    }
    
    # These two tests assume you are running this outside daylight savings (during the winter) .. hacky but good way to showcase the syntax ;)
    It 'It should not be daylight savings time' {
        $responseContent.isDayLightSavingsTime | Should -Not -Be $true
    }
    
    It 'It should not be daylight savings time another way' {
        $responseContent.isDayLightSavingsTime | Should -BeFalse
    }
}
4

2 回答 2

2

我认为Daniel 的回答很棒,但是如果您正在处理大型或共享存储库,那么您也只需要小心管理这些 XML 文件。我使用的另一种选择是使用真实响应为所有返回的对象创建一个大型 Json 文件。它可以被导入,也可以BeforeAll根据BeforeDiscovery你的测试结构来导入。

我补充答案的原因实际上也只是为了涵盖错误响应,因为拥有显示您如何处理 REST 调用失败的测试用例非常重要。包装Invoke-WebRequest在您自己的函数中可能对返回个性化错误、处理标头响应以及为站点名称或允许的 API 路径集提供常量很有用。例如,根据 PowerShell 的版本,这就是我处理 404 的方式。

Context " When a path does not exist in the API" {
    BeforeAll {
        Mock Invoke-WebRequest {
            # Use the actual types returned by your function or directly from Invoke-WebRequest.
            if ($PSVersionTable.PSEdition -eq "Desktop") {
                $WR = New-MockObject -Type 'System.Net.HttpWebResponse'
                $Code = [System.Net.HttpStatusCode]::NotFound
                # Use Add-Member because StatusCode is a read-only field on HttpWebResponse
                $WR | Add-Member -MemberType NoteProperty -Name StatusCode -Value $Code -Force
                $Status = [System.Net.WebExceptionStatus]::ProtocolError
                $Ex = [System.Net.WebException]::new("404", $null, $Status, $WR)
            }
            else {
                $Message = [System.Net.Http.HttpResponseMessage]::new()
                $Message.StatusCode = [System.Net.HttpStatusCode]::NotFound
                $Ex = [Microsoft.PowerShell.Commands.HttpResponseException]::new("404", $Message)
            }
            throw $Ex
        } -ParameterFilter {
            $Uri -eq "http://worldclockapi.com/api/json/utc/NEVER" -and
            $Method -eq "Get"
        }

        $GetRestTimeParams = @{
            Uri   = "http://worldclockapi.com/api/json/utc/NEVER"
            Method = "Get"
        }
    }

    It " Get-RestTime should not run successfully" {
        { Get-RestTime @GetRestTimeParams } | Should -Throw
    }

    It " Get-RestTime should throw a 404 error" {
        $ShouldParams = @{
            # Use the actual types returned by your function or directly from Invoke-WebRequest.
            ExceptionType   = [System.Net.WebException]
            ExpectedMessage = "404: NotFound"
        }
        {
            Get-RestTime @GetRestTimeParams
        } | Should -Throw @ShouldParams
    }
}
于 2021-07-26T07:42:18.093 回答
2

使用真实响应作为模拟输出的模板可能是最简单的。

Invoke-WebRequest -Method 'GET' -Uri 'http://worldclockapi.com/api/json/utc/now' | 
    Export-Clixml .\response.xml

上面的命令会将来自 API 的真实响应序列化到文件中。

现在我们可以导入文件以与我们的模拟一起使用。所要做的就是使用Mock命令来定义我们的模拟。

Mock

  • -CommandName:我们正在模拟的命令
  • -ParameterFilter: 一个可选过滤器,用于将模拟行为限制为仅使用 CommandName ,其中传递给命令的参数值通过过滤器。此 ScriptBlock 必须返回一个布尔值。
  • -MockWith:一个 ScriptBlock 指定将用于模拟 CommandName 的行为。默认值为空的 ScriptBlock。在这里,我们将文件导入为我们的输出。
Mock -CommandName Invoke-WebRequest -ParameterFilter { $Method -eq 'GET' } -MockWith { Import-Clixml .\response.xml }

现在,Invoke-WebRequest使用-Method 'Get'我们的 mock 调用 when 将被调用,并将返回我们使用Import-CliXml(或其他方法 - json、xml 等)导入的对象。
注意:mock 命令必须在对我们正在模拟的命令的任何调用之前出现。另请注意即使在使用该命令的其他函数内部也会调用模拟。

    BeforeAll {
        Mock -CommandName Invoke-WebRequest -ParameterFilter { $Method -eq 'GET' } -MockWith { Import-Clixml .\response.xml }

        # on this next line our mock will be called instead and will return our prepared object
        $response = Invoke-WebRequest -Method 'GET' -Uri 'http://worldclockapi.com/api/json/utc/now'
        $responseContent = $response.Content | ConvertFrom-Json
        Write-Output $responseContent
    }
于 2021-07-26T06:27:22.903 回答