sameerk.HttpPlaygroundServer 2.0.0

dotnet add package sameerk.HttpPlaygroundServer --version 2.0.0
                    
NuGet\Install-Package sameerk.HttpPlaygroundServer -Version 2.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="sameerk.HttpPlaygroundServer" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="sameerk.HttpPlaygroundServer" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="sameerk.HttpPlaygroundServer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add sameerk.HttpPlaygroundServer --version 2.0.0
                    
#r "nuget: sameerk.HttpPlaygroundServer, 2.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.
#:package sameerk.HttpPlaygroundServer@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=sameerk.HttpPlaygroundServer&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=sameerk.HttpPlaygroundServer&version=2.0.0
                    
Install as a Cake Tool

HttpPlaygroundServer

Enables functional testing of HTTP client workflows using a local server that records request–response pairs and provides the ability to verify call sequences.


Functional testing: the primary use case

HttpPlaygroundServer is designed for “mini” functional testing of HTTP client workflows—especially when your code calls multiple backends (e.g., your API + a 3rd‑party API) and you want to validate:

  • Call order (sequence of requests)
  • Branching behavior (different sequences based on conditions)
  • Real HTTP plumbing (URLs, headers, serialization) without a live backend
  • Server simulation (custom logic or file-based responses)

This package is intended for console apps and custom harnesses (it does not require or integrate with xUnit/NUnit).


Installation

dotnet add package sameerk.HttpPlaygroundServer

Functional testing with server simulation (sample)

This is a simplified excerpt of the sample you provided. It demonstrates:

  • Starting the playground server with a custom simulator
  • Running a workflow twice (photo missing vs. photo available)
  • Verifying the sequence of recorded request–response pairs via RequestResponses

Start the server and run functional workflows

/// <summary>
/// "Mini" functional testing + server simulation:
/// Tests CatManager.CreateCatWithPhoto which calls a backend API + external photo API.
/// Verifies requests are called in a certain order based on conditions.
/// </summary>
internal class FunctionalTestingWithServerSimulation
{
    private static HttpPlaygoundServer _playgroundServer = new(new MultiServerSimulator());

    internal static async Task Run()
    {
        CancellationTokenSource cts = new();
        ManualResetEventSlim serverStarted = new();

        // optional: disable request logging to disk
        _playgroundServer.IsRequestLoggingEnabled = false;

        _ = Task.Run(async () =>
            await _playgroundServer.StartServer(serverStarted, cts.Token).ConfigureAwait(false)
        );

        serverStarted.Wait();

        // Workflow: photo NOT available
        bool result = await Test_Post_Cat_Valid_Id_No_Photo().ConfigureAwait(false);
        Console.WriteLine($"{(result ? string.Empty : "--- FAILED- -->")} Test_Post_Cat_Valid_Id_No_Photo {result}");

        // Workflow: photo available
        result = await Test_Post_Cat_Valid_Id_And_Photo().ConfigureAwait(false);
        Console.WriteLine($"{(result ? string.Empty : "--- FAILED- -->")} Test_Post_Cat_Valid_Id_And_Photo {result}");

        cts.Cancel();
        _playgroundServer.StopServer();
    }

    private static async Task<bool> Test_Post_Cat_Valid_Id_No_Photo()
    {
        _playgroundServer.ClearRequestResponses();

        ServerConfig.StorageFolder = Path.Combine(Directory.GetCurrentDirectory(), "TestData/Functional");

        // Route 3rd-party API client to the playground server
        PhotoClient.Instance.BaseUrl = ServerConfig.HostName;
        PhotoClient.Instance.Port = ServerConfig.Port;

        CatManager catCaller = new();
        HttpStatusCode result = await catCaller.CreateCatWithPhoto(1, "No Photo").ConfigureAwait(false);

        if (result != HttpStatusCode.Created) return false;

        // Verify call sequence (POST -> GET)
        return (_playgroundServer.RequestResponses.Count == 2 &&
            ValidateRequest(_playgroundServer.RequestResponses[0].Item1, HttpMethod.Post, RequestSender.RestPath) &&
            ValidateRequest(_playgroundServer.RequestResponses[1].Item1, HttpMethod.Get, PhotoClient.RelativeURL + "/1"));
    }

    private static async Task<bool> Test_Post_Cat_Valid_Id_And_Photo()
    {
        _playgroundServer.ClearRequestResponses();

        ServerConfig.StorageFolder = Path.Combine(Directory.GetCurrentDirectory(), "TestData/Functional");

        PhotoClient.Instance.BaseUrl = ServerConfig.HostName;
        PhotoClient.Instance.Port = ServerConfig.Port;

        CatManager catCaller = new();
        HttpStatusCode result = await catCaller.CreateCatWithPhoto(3, "With Photo").ConfigureAwait(false);

        if (result != HttpStatusCode.OK) return false;

        // Verify call sequence (POST -> GET -> PATCH)
        return (_playgroundServer.RequestResponses.Count == 3 &&
            ValidateRequest(_playgroundServer.RequestResponses[0].Item1, HttpMethod.Post, RequestSender.RestPath) &&
            ValidateRequest(_playgroundServer.RequestResponses[1].Item1, HttpMethod.Get, PhotoClient.RelativeURL + "/3") &&
            ValidateRequest(_playgroundServer.RequestResponses[2].Item1, HttpMethod.Patch, RequestSender.RestPath));
    }

    private static bool ValidateRequest(RequestModel req, HttpMethod verb, string urlEnd)
        => req.Verb == verb.Method && req.URL.EndsWith(urlEnd);
}

Server simulation: custom logic + file-based responses

HttpPlaygroundServer supports server simulation by letting you plug in a request processor. The example below shows a multi-server simulator that routes requests to either:

  • Cat API simulation (file-based responses via RetrieveByFile(...))
  • Photo API simulation (simple in-memory logic)

MultiServerSimulator (excerpt)

internal class MultiServerSimulator : HttpRequestProcessor
{
    protected override Task<ResponseModel> SimulateServerHandling(RequestModel rs)
    {
        // Redirect for cat or photo based on URL content
        if (rs.URL.Contains("cat"))
        {
            return RetreiveCatResponse(rs);
        }

        return RetreivePhotoResponse(rs);
    }

    private Task<ResponseModel> RetreivePhotoResponse(RequestModel rs)
    {
        ResponseModel rm = new();
        rm.Headers.Add("Content-Type", "plain/text; charset=utf-8");

        int catIdInd = rs.URL.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase) + 1;
        string catId = rs.URL.Substring(catIdInd);

        if (catId == "-1")
        {
            rm.Body = "Not valid";
            rm.StatusCode = HttpStatusCode.BadRequest;
        }
        else if (catId == "3")
        {
            rm.Body = $"https://yourcatphoto/Cat{catId}";
            rm.StatusCode = HttpStatusCode.OK;
        }
        else
        {
            rm.StatusCode = HttpStatusCode.NotFound;
            rm.Body = null;
        }

        var ret = new Task<ResponseModel>(() => rm);
        ret.RunSynchronously();
        return ret;
    }

    private Task<ResponseModel> RetreiveCatResponse(RequestModel rs)
    {
        if (rs.Verb == HttpMethod.Get.Method)
        {
            int catEndInd = rs.URL.IndexOf("cats", StringComparison.InvariantCultureIgnoreCase) + 4;

            if (rs.URL.Contains("-1"))
            {
                return base.RetrieveByFile("404.json", rs.URL.Remove(catEndInd));
            }

            return base.RetrieveByFile("CatGet.json", rs.URL.Remove(catEndInd));
        }
        else if (rs.Verb == HttpMethod.Post.Method)
        {
            CatModel? catModel = JsonSerializer.Deserialize<CatModel?>(rs.Body);
            string responseFile = (catModel?.Id == -1) ? "400.json" : "CatPost.json";
            return base.RetrieveByFile(responseFile);
        }
        else
        {
            return base.RetrieveByFile("Patch.json");
        }
    }
}

Why this matters for functional testing: you can simulate realistic backend behaviors (including branching) while still keeping the system local and deterministic.


What to verify in functional tests

HttpPlaygroundServer records request–response pairs; your harness verifies behavior.

Typical checks:

  • Sequence: POST → GET (photo missing), POST → GET → PATCH (photo available)
  • Count: expected number of calls
  • Targets: correct endpoint path suffixes
  • Verb correctness: POST/GET/PATCH as expected

Notes

  • Use ClearRequestResponses() before each workflow run.
  • You can enable/disable disk logging via IsRequestLoggingEnabled.
  • ServerConfig.StorageFolder controls where file-based responses and logs live.

Source and samples

GitHub repository (source + console samples): https://github.com/sameerkapps/HttpPlayground

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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 was computed.  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 was computed.  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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 0 1/12/2026
1.0.0 213 11/23/2025

Initial release