HttpRequest.Spy 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package HttpRequest.Spy --version 1.0.0                
NuGet\Install-Package HttpRequest.Spy -Version 1.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HttpRequest.Spy" Version="1.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add HttpRequest.Spy --version 1.0.0                
#r "nuget: HttpRequest.Spy, 1.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install HttpRequest.Spy as a Cake Addin
#addin nuget:?package=HttpRequest.Spy&version=1.0.0

// Install HttpRequest.Spy as a Cake Tool
#tool nuget:?package=HttpRequest.Spy&version=1.0.0                

HttpRequestSpy

example workflow

HttpRequestSpy is a tool aiming to test outgoing HttpRequest sent via an HttpClient.

It compares all sent http requests to an expected request definition and provides a user friendly error message when no recorded httprequest matches the expected one.

This tool is useful when you want to test your outgoing http requests but you don't want to mock the http client.

Installation

dotnet add package HttpRequest.Spy

Usage

    [Fact]
    public async Task HttpRequest_should_be_sent()
    {
        // Arrange
        var spy = HttpRequestSpy.Create();
        using var httpClient = new HttpClient(new SpyHttpMessageHandler(spy));

        var instance = new TypedHttpClient(httpClient);

        // Act
        await instance.MakeHttpRequest();

        // Assert
        spy.HasRecordedRequests(1);

        spy.AGetRequestTo("/some/ressource")
           .WithQuery(new {
               id = "12"
           })
           .OccurredOnce();
    }


    public class TypedHttpClient(HttpClient httpClient) {
        public async Task<HttpResponseMessage> MakeHttpRequest() {
            return await httpClient.GetAsync("/some/ressource?id=12");
        }
    }

Features

Fluent API

The HttpRequestSpy provides a fluent API to help you define your expected http request.

    spy.AGetRequestTo("/some/ressource")
       .WithQuery(new {
           id = "12"
       })
       .OccurredOnce();

User friendly error messages

One of the main idea behind this tool is to provide a user friendly error message when no recorded http request matches the expected one.

    spy.APostRequestTo("/some/resource")
        .WithJsonPayload(new {
            prop1 = 1
        })
        .WithQuery(new {
            id = "12"
        })
        .OccurredOnce();
 ⚠ Unexpected number of recorded requests matching expectations. Expected : 1. Actual : 0

Expectations: 
 ✔ Method : POST
 ✔ Expected URL : /some/resource
 ✔ Payload : {"prop1":1}
 ✔ Query { id = 12 }

2 Recorded requests: 
0/ GET http://domain/path/to/resource
  ⚠ HttpMethod does not match:
 - Expected : POST
 - Actual : GET
  ⚠ URL does not match:
 - Expected : /some/resource
 - Actual : /path/to/resource
  ⚠ Payload is empty:
 - Expected : {"prop1":1}
 - Actual : [null]
  ⚠ Query does not match :
   -> Missing property $.id

1/ POST http://domain/path/to/resource
  ⚠ URL does not match:
 - Expected : /some/resource
 - Actual : /path/to/resource
  ⚠ Payload is empty:
 - Expected : {"prop1":1}
 - Actual : [null]
  ⚠ Query does not match :
   -> Missing property $.id

Occurrences

You can define the number of occurences of your expected request.

    spy.AGetRequestTo("/some/ressource")
       .OccurredOnce();

    spy.AGetRequestTo("/some/ressource")
       .OccurredTwice();

    spy.AGetRequestTo("/some/ressource")
       .Occurred(3);
    
    spy.AGetRequestTo("/some/ressource")
       .NeverOccurred();

Number of overall recorded requests

You can define the number of overall recorded requests.

    spy.HasRecordedRequests(5);
       .NeverOccurred();

This codes checks that 5 requests have been recorded regardless of the content of the requests.

Absolute path and relative url

You can define the absolute path or the relative url of your expected request.

    spy.AGetRequestTo("/some/ressource")
       .OccurredOnce();
    spy.AGetRequestTo("https://myapi.com/some/ressource")
       .OccurredOnce();

GET, POST, PUT, DELETE, PATCH http verbs

You can define the http verb of your expected request.

    spy.AGetRequestTo("/some/ressource")
       .OccurredOnce();
    
    spy.APostRequestTo("/some/ressource")
       .OccurredOnce();
    
    spy.APutRequestTo("/some/ressource")
       .OccurredOnce();
    
    spy.APatchRequestTo("/some/ressource")
       .OccurredOnce();
    
    spy.ADeleteRequestTo("/some/ressource")
       .OccurredOnce();

Query parameters

You can define the query parameters of your expected request as an anonymous object.

    spy.AGetRequestTo("/some/ressource")
       .WithQuery(new {
           id = "12",
           date = "2024-01-05"
       })
        /// Or
       .WithQuery("id=12&date=2024-01-05")
        /// Or
       .WithQueryParam("id", "12")
       .WithQueryParam("date", "2024-01-05")
        
       .OccurredOnce();

Body / Payload

Only for POST, PUT, PATCH requests

You can define the body of your expected request as an anonymous object or target a specific property.

Note: payloads are compared using a custom comparison tool that will do a deep equal and returns all the mismatching properties. See the Comparison folder.

Note 2 : By default, in the error message the payload of the recorded request won't be displayed because it would create a too long message. We only display the mismatching properties. Sometimes, for debugging purpose, you might want to display the full payload. To do so, you can pass true to the logRecordedRequestPayload parameter.

Check Json payload

See HttpRequestSpyWhenJsonPayloadShould.cs file for all use cases.

    spy.APostRequestTo("/some/ressource")       
       .WithJsonPayload(new
                    {
                        prop1 = 1,
                        prop2 = "value",
                        subProp = new
                        {
                            subProp1 = "subValue"
                        }
                    })
        
        // Or
        .WithJsonPayloadProperty("prop1") // Will check that the property exists
        .WithJsonPayloadProperty("prop1", 1)
        .WithJsonPayloadProperty("prop2", "value")
        .WithJsonPayloadProperty("subProp", new
                        {
                            subProp1 = "subValue"
                        })
        
        
       .OccurredOnce();

Note 1 : To compare payloads we use System.Text.Json. It is possible to pass a custom JsonSerializerOptions to the WithJsonPayloadProperty and WithJsonPayload methods.

Note 2: We're not considering using Newtonsoft.Json at all and it is not possible to change the serializer.

Json payload matching JsonSchema

Using JsonSchema, you can check that the payload matches a specific schema.

    spy.APostRequestTo("/some/ressource")       
        .WithPayloadMatchingJsonSchema("path/to/schema.json")
       .OccurredOnce();

Note: it is also possible to specify the schema as a string.

Check Xml payload

See HttpRequestSpyWhenXmlPayloadShould.cs file for all use cases.

    spy.APostRequestTo("/some/ressource")
       .WithXmlPayload(new
                    {
                        prop1 = 1,
                        prop2 = "value",
                        subProp = new
                        {
                            subProp1 = "subValue"
                        }
                    })
        
        // Or
        .WithXmlPayloadProperty("/_x003F_anonymous_x003F_/prop1") // Will check that the property exists
        .WithXmlPayloadProperty("/_x003F_anonymous_x003F_/prop1", 1)
        .WithXmlPayloadProperty("/_x003F_anonymous_x003F_/prop2", "value")
        .WithXmlPayloadProperty("/_x003F_anonymous_x003F_/subProp", new
                        {
                            subProp1 = "subValue"
                        })
        // Or  :
        .WithXmlPayloadProperty("/_x003F_anonymous_x003F_/subProp/subProp1", "subValue")
        
        
       .OccurredOnce();

Note 1: the xpath used is a simplified xpath. It is not a full xpath implementation. It ignores namespaces and attributes for readability purpose. We do not need such complex features.

Note 2: in a future version, we might ignore the type node for anonymous objects.

Note 3: XSD validation is not supported yet.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on HttpRequest.Spy:

Package Downloads
sas.simulators.http

Includes objects and helpers to create HttpClient simulators easily when using the Scenario/API/Simulators concept.

sas.simulators.soap

Experimental, not production-ready yet.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.10 82 11/19/2024
1.0.7 77 10/25/2024
1.0.0 890 1/5/2024
0.0.28 130 1/5/2024
0.0.27 121 1/5/2024