RouteStub.Testing
0.0.1
See the version list below for details.
dotnet add package RouteStub.Testing --version 0.0.1
NuGet\Install-Package RouteStub.Testing -Version 0.0.1
<PackageReference Include="RouteStub.Testing" Version="0.0.1" />
<PackageVersion Include="RouteStub.Testing" Version="0.0.1" />
<PackageReference Include="RouteStub.Testing" />
paket add RouteStub.Testing --version 0.0.1
#r "nuget: RouteStub.Testing, 0.0.1"
#:package RouteStub.Testing@0.0.1
#addin nuget:?package=RouteStub.Testing&version=0.0.1
#tool nuget:?package=RouteStub.Testing&version=0.0.1
Project Scope
Vision
Build a lightweight, convention-based HTTP stub server for .NET.
Instead of defining routes through configuration files or fluent APIs, HTTP requests are resolved directly from a hierarchical collection of stub artifacts. The convention is the configuration.
The goal is to make creating a fake HTTP API as simple as creating folders and files.
For example:
GET /api/weather/sunny
maps to:
stubs/
└── api/
└── weather/
└── sunny/
├── GET.content.json
└── GET.response.json
No request mappings. No DSL. No route definitions.
If you know the request URL, you already know where the stub belongs.
Target Audience
The library is intended for developers who need a lightweight HTTP endpoint for:
- Integration tests
- Frontend development
- Local development
- Console applications
- Mobile development
- API prototyping
- Testing HTTP clients
It should be easy to point any existing HttpClient to the stub server without changing application code.
Design Principles
- Convention over configuration
- Real HTTP instead of mocked
HttpClient - Small and opinionated
- Fast startup
- Deterministic request resolution
- Easy to understand
- Easy to debug
- Storage-agnostic architecture
Core Concepts
Convention-based routing
Every URL segment maps directly to a folder.
Static fixtures
Responses are represented as files.
Each endpoint consists of:
- A content file containing the response body.
- A response metadata file describing status code, headers, delays, and other HTTP metadata.
Request Resolution
The resolver determines the correct stub by mapping the incoming HTTP request to a hierarchical fixture structure.
The following parts of an HTTP request can influence resolution:
- HTTP method
- URL path segments
- Query parameters
- Wildcard matching rules
The goal is to make common API variations possible without introducing a configuration language.
Query Parameters
Query parameters are treated as part of the request path when resolving stubs.
Example request:
GET /api/weather?city=London
can resolve to:
stubs/
└── api/
└── weather/
└── London/
├── GET.content.json
└── GET.response.json
This allows different responses to be provided for different query parameter combinations while keeping the fixture structure human-readable.
Wildcard Matching
Wildcards are supported to simplify scenarios where exact matching would create unnecessary duplication.
Wildcards are useful for:
- Dynamic route parameters
- Variable identifiers
- Optional query values
- Groups of similar responses
Example:
Instead of creating:
api/
└── users/
├── 123/
├── 456/
└── 789/
a wildcard fixture can be created:
api/
└── users/
└── *
├── GET.content.json
└── GET.response.json
A request such as:
GET /api/users/123
will resolve against the wildcard fixture.
Resolution Priority
When multiple fixtures could match a request, the resolver prefers the most specific match.
The general priority is:
- Exact path match
- Wildcard path match
This ensures specific scenarios override generic defaults.
Example:
api/
└── users/
├── 123/
│ └── GET.content.json
│
└── *
└── GET.content.json
Request:
GET /api/users/123
will use:
users/123/
because it is more specific than:
users/*
Architecture
The project consists of a small, reusable resolution engine.
HTTP Request
│
▼
Convention Resolver
│
▼
Stub Repository
│
├── Local filesystem
├── Azure Files
├── Memory
└── Future implementations
The resolver should never depend on the storage implementation.
Its only responsibility is resolving an incoming request to the most appropriate stub.
Consumption
The same engine powers multiple hosting models.
- ASP.NET Core middleware
- Standalone CLI
- Test host for integration tests
Every host should behave identically because they all use the same resolver.
ASP.NET Core
Install the ASP.NET Core integration package from NuGet.org:
dotnet add package RouteStub.AspNetCore
Or add the package reference directly when pinning a version in the project file:
<PackageReference Include="RouteStub.AspNetCore" Version="1.0.0" />
For the shortest setup, mount a fixture directory directly after building the application:
using RouteStub.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseStubServer("./stubs");
app.Run();
Use service registration when the fixture path comes from configuration or
when the application supplies a custom IStubRepository or IStubResolver:
using RouteStub.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddStubServer(options =>
{
options.FixturePath = "./stubs";
});
var app = builder.Build();
app.UseStubServer();
app.Run();
RouteStub adds its filesystem repository and resolver only when the application
has not already registered replacements. UseStubServer is terminal
middleware, so register authentication, logging, or other middleware that must
run for stub requests before it.
Integration tests
Install the framework-neutral testing package from a test project:
dotnet add package RouteStub.Testing
Or add the package reference directly when pinning a version in the project file:
<PackageReference Include="RouteStub.Testing" Version="1.0.0" />
Start a real loopback HTTP endpoint on an available ephemeral port and assign its address to the client under test:
using RouteStub.Testing;
await using var server = await RouteStubTestServer.StartAsync("./stubs");
using var client = new HttpClient
{
BaseAddress = server.BaseAddress
};
var response = await client.GetAsync("api/weather");
RouteStubTestServer is independent of any test framework. It supports
startup cancellation and implements IAsyncDisposable, so it works with the
lifecycle conventions of MSTest, xUnit, NUnit, or a custom test harness.
Runnable samples
Complete examples for both integration models are available under
samples. The directory contains a dedicated
RouteStub.Samples.slnx solution, an ASP.NET Core host with representative
fixtures, and an integration test that configures an application HTTP client
from an ephemeral RouteStubTestServer endpoint.
dotnet build samples\RouteStub.Samples.slnx
dotnet test samples\RouteStub.Testing.Sample
dotnet run --project samples\RouteStub.AspNetCore.Sample
Standalone server
The CLI can be run directly from this repository. It uses the same middleware adapter as the ASP.NET Core and testing packages.
Run the fixture directory at ./stubs on the default
http://localhost:5000 address:
dotnet run --project src/RouteStub.Cli -- serve
Provide a different fixture directory or listening address when needed:
dotnet run --project src/RouteStub.Cli -- serve ./fixtures --url http://localhost:5050
Content files support JSON, XML, HTML, PDF, plain text, CSS, JavaScript, CSV,
YAML, PNG, JPEG, GIF, SVG, and WebP. The file extension determines the HTTP
Content-Type header.
Publishing a NuGet release
Package publication is performed by
publish-nuget.yml when a GitHub Release
is published. Maintainers must configure a repository Actions secret named
NUGET_API_KEY containing a scoped NuGet.org API key that can create new
versions of RouteStub.Core, RouteStub.AspNetCore, RouteStub.Server, and
RouteStub.Testing.
Create and publish a GitHub Release with a stable semantic-version tag in exact
vMAJOR.MINOR.PATCH form, such as v1.2.3. The workflow validates the tag,
restores, builds, and tests the full solution, and then packs all four projects
with the normalized 1.2.3 package version. It verifies the resulting package
set and same-version dependency graph before any package is pushed to
NuGet.org. Branch pushes, pull requests, draft releases, and prerelease-style
tags do not publish packages.
NuGet package versions are immutable. If a workflow run stops after publishing
only part of the package set, rerun the same release workflow after correcting
the failure. Publication uses --skip-duplicate, so packages accepted during
the previous attempt are skipped and the remaining packages can be published
safely. The API key is supplied only to the final publication step and is not
stored in repository files.
Non-Goals
This project is intentionally not trying to replace WireMock.
It will not aim to provide:
- Complex request matching
- Configuration-heavy route definitions
- Recording/proxying
- Stateful workflows
- Contract testing
- JavaScript scripting
- API virtualization
Those tools already exist.
This project focuses on the 90% use case:
"I need an HTTP endpoint that returns realistic responses with as little setup as possible."
Guiding Principle
If creating or changing a stub requires learning a custom configuration language, the project has failed.
The ideal workflow should be:
- Create a folder.
- Copy an existing stub.
- Modify the response.
- Start the server.
- Your application works.
Nothing more.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- RouteStub.Server (>= 0.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.