xUnitV3LoadFramework 2.0.0.57
dotnet add package xUnitV3LoadFramework --version 2.0.0.57
NuGet\Install-Package xUnitV3LoadFramework -Version 2.0.0.57
<PackageReference Include="xUnitV3LoadFramework" Version="2.0.0.57" />
<PackageVersion Include="xUnitV3LoadFramework" Version="2.0.0.57" />
<PackageReference Include="xUnitV3LoadFramework" />
paket add xUnitV3LoadFramework --version 2.0.0.57
#r "nuget: xUnitV3LoadFramework, 2.0.0.57"
#:package xUnitV3LoadFramework@2.0.0.57
#addin nuget:?package=xUnitV3LoadFramework&version=2.0.0.57
#tool nuget:?package=xUnitV3LoadFramework&version=2.0.0.57
xUnitV3LoadFramework
xUnit v3-native load-style test runner for executing actions concurrently over a duration and reporting throughput/success/failure.
What It Is / What It Isn't
Best for:
- CI performance smoke tests
- Concurrency and regression checks
- Integration-style load tests inside
dotnet test - Quick validation that your API handles N concurrent requests
Not for:
- Distributed load generation across machines
- Protocol-specific clients (HTTP/2, gRPC, WebSocket)
- Full observability platform with dashboards
- Replacing dedicated load testing tools (k6, Locust, NBomber)
Install
dotnet add package xUnitV3LoadFramework
Quickstart
Attribute-Based
using xUnitV3LoadFramework.Attributes;
using xUnitV3LoadFramework.Extensions;
public class ApiLoadTests
{
private static readonly HttpClient _httpClient = new();
[Load(concurrency: 5, duration: 3000, interval: 500)]
public async Task Api_Should_Handle_Concurrent_Requests()
{
var result = await LoadTestRunner.ExecuteAsync(async () =>
{
var response = await _httpClient.GetAsync("https://api.example.com/health");
return response.IsSuccessStatusCode;
});
Assert.True(result.Success > 0);
Assert.True(result.RequestsPerSecond > 1);
}
}
Fluent API
using xUnitV3LoadFramework.Extensions;
public class ApiLoadTests
{
private static readonly HttpClient _httpClient = new();
[Fact]
public async Task Api_Load_Test_Fluent()
{
var result = await LoadTestRunner.Create()
.WithName("HealthCheck_Load")
.WithConcurrency(10)
.WithDuration(TimeSpan.FromSeconds(5))
.WithInterval(TimeSpan.FromMilliseconds(200))
.RunAsync(async () =>
{
var response = await _httpClient.GetAsync("https://api.example.com/health");
response.EnsureSuccessStatusCode();
});
Assert.True(result.Success >= result.Total * 0.95);
}
}
Sample Output
Load test 'HealthCheck_Load' completed:
Total executions: 50
Successful executions: 48
Failed executions: 2
Execution time: 5.12 seconds
Requests per second: 9.77
Average latency: 102.34ms
Success rate: 96.00%
Core Concepts
| Setting | Description |
|---|---|
| Concurrency | Number of concurrent operations launched per interval |
| Duration | Total time the load test runs |
| Interval | Time between launching batches of concurrent operations |
| TerminationMode | How the test stops: Duration (immediate), CompleteCurrentInterval (finish current batch), or StrictDuration (exact timing) |
| GracefulStopTimeout | Max time to wait for in-flight requests after duration expires. Default: 30% of duration, bounded 5-60s |
| Success | Action returns true or completes without exception |
| Failure | Action returns false or throws an exception |
| RequestsPerSecond | Total / Time — completed operations per second |
How Interval Works
Every Interval, the framework launches Concurrency concurrent operations. For example:
Concurrency: 5, Duration: 3s, Interval: 500ms= 6 batches × 5 operations = ~30 total operations
Advanced Configuration
Using LoadExecutionPlan Directly
For full control, use LoadExecutionPlan with LoadRunner.Run():
using LoadSurge.Models;
using LoadSurge.Runner;
[Fact]
public async Task Advanced_Load_Test()
{
var plan = new LoadExecutionPlan
{
Name = "Database_Connection_Pool",
Settings = new LoadSettings
{
Concurrency = 20,
Duration = TimeSpan.FromSeconds(30),
Interval = TimeSpan.FromMilliseconds(100),
TerminationMode = TerminationMode.CompleteCurrentInterval,
GracefulStopTimeout = TimeSpan.FromSeconds(10)
},
Action = async () =>
{
using var conn = new SqlConnection(connectionString);
await conn.OpenAsync();
return true;
}
};
var result = await LoadRunner.Run(plan);
Assert.True(result.Success >= result.Total * 0.99);
}
Termination Modes
| Mode | Behavior |
|---|---|
Duration |
Stops immediately when duration expires (default) |
CompleteCurrentInterval |
Waits for current batch to finish before stopping |
StrictDuration |
Strict timing — may cut off final batch |
MaxIterations (Fluent API)
Stop after a fixed number of operations regardless of duration:
var result = await LoadTestRunner.Create()
.WithConcurrency(10)
.WithDuration(TimeSpan.FromMinutes(5))
.WithMaxIterations(1000) // Stop after 1000 operations
.RunAsync(async () => { /* ... */ });
Assertions / CI Gating
Use LoadResult fields to fail tests based on performance criteria:
var result = await LoadTestRunner.Create()
.WithConcurrency(10)
.WithDuration(TimeSpan.FromSeconds(10))
.RunAsync(async () => { /* ... */ });
// Success rate gate
var successRate = (double)result.Success / result.Total;
Assert.True(successRate >= 0.99, $"Success rate {successRate:P} below 99%");
// Throughput gate
Assert.True(result.RequestsPerSecond >= 50, $"RPS {result.RequestsPerSecond} below 50");
// Latency gate
Assert.True(result.Percentile95Latency < 500, $"P95 latency {result.Percentile95Latency}ms exceeds 500ms");
Assert.True(result.AverageLatency < 200, $"Avg latency {result.AverageLatency}ms exceeds 200ms");
Available Result Fields
Total,Success,Failure— countsTime— execution time in secondsRequestsPerSecond— throughputAverageLatency,MinLatency,MaxLatency— in millisecondsMedianLatency,Percentile95Latency,Percentile99Latency— percentiles in msPeakMemoryUsage— bytes
Safety & Gotchas
- Thread-safety: Your action runs concurrently. Avoid shared mutable state unless protected.
- Reuse HttpClient: Create a single
static readonly HttpClient— don't instantiate per request. - Start low: Begin with low concurrency and short duration. Increase gradually.
- Timeouts: Add your own timeout in the action. The framework won't kill hung operations:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); await httpClient.GetAsync(url, cts.Token); - Cancellation: The framework uses
GracefulStopTimeoutto wait for in-flight requests. Long-running actions without cancellation support may delay test completion. - CI filtering: Use
dotnet test --filter "FullyQualifiedName~LoadTests"to run only load tests or exclude them from fast CI runs.
When to Choose This
Use this framework when:
- You want load tests as part of
dotnet testwithout extra tooling - You need quick concurrency smoke tests in CI
- Your tests are integration-style (HTTP calls, database queries)
- You want xUnit v3 native attributes and test discovery
Use a dedicated tool (k6, Locust, NBomber) when:
- You need distributed load from multiple machines
- You need protocol-specific features (HTTP/2 multiplexing, WebSocket)
- You need real-time dashboards and detailed analytics
- You're testing at scale (thousands of concurrent users)
Fun Explanation (optional)
Think of this like a playground stress test. You set:
- How many kids play at once (
Concurrency) - How long the playground is open (
Duration) - How often new groups arrive (
Interval)
The framework tells you how many kids had fun (success), how many fell off the swings (failure), and how fast the line moved (RPS).
Requirements
- .NET 8.0+
- xUnit v3
Contributing
PRs welcome. Open an issue for bugs or feature requests.
Made by Vasyl
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. 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. |
-
net8.0
- LoadSurge (>= 1.0.0.45)
- Microsoft.SourceLink.GitHub (>= 8.0.0)
- xunit.v3.extensibility.core (>= 3.2.1)
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.57 | 22 | 12/13/2025 |
| 2.0.0.56 | 22 | 12/13/2025 |
| 2.0.0.55 | 184 | 12/5/2025 |
| 2.0.0.54 | 176 | 12/4/2025 |
| 2.0.0.53 | 648 | 12/1/2025 |
| 2.0.0.52 | 554 | 12/1/2025 |
| 2.0.0.51 | 275 | 11/16/2025 |
| 2.0.0.50 | 269 | 11/16/2025 |
| 2.0.0.49 | 268 | 11/16/2025 |
| 2.0.0.48 | 218 | 11/16/2025 |
| 2.0.0.47 | 204 | 11/14/2025 |
| 2.0.0.46 | 197 | 11/14/2025 |
| 2.0.0.45 | 200 | 11/14/2025 |
| 2.0.0.44 | 203 | 11/14/2025 |
| 2.0.0.43 | 203 | 11/14/2025 |
| 2.0.0.42 | 206 | 11/14/2025 |
| 2.0.0.41 | 179 | 10/22/2025 |
| 2.0.0.38 | 165 | 10/21/2025 |
| 2.0.0.36 | 178 | 9/12/2025 |
| 2.0.0.35 | 180 | 9/12/2025 |
| 2.0.0.27 | 388 | 8/5/2025 |
| 2.0.0.22 | 144 | 8/3/2025 |
| 2.0.0.21 | 148 | 8/3/2025 |
| 2.0.0.20 | 152 | 8/3/2025 |
| 2.0.0.19 | 148 | 8/3/2025 |
| 1.0.0.95 | 67 | 8/2/2025 |
| 1.0.0.94 | 59 | 8/1/2025 |
| 1.0.0.93 | 61 | 8/1/2025 |
| 1.0.0.92 | 138 | 7/29/2025 |
| 1.0.0.91 | 147 | 7/28/2025 |
| 1.0.0.90 | 143 | 7/28/2025 |
| 1.0.0.87 | 369 | 7/25/2025 |
| 1.0.0.86 | 369 | 7/25/2025 |
| 1.0.0.85 | 499 | 7/24/2025 |
| 1.0.0.84 | 490 | 7/24/2025 |
| 1.0.0.83 | 544 | 7/23/2025 |
| 1.0.0.81 | 549 | 7/22/2025 |
| 1.0.0.80 | 542 | 7/22/2025 |
| 1.0.0.79 | 547 | 7/22/2025 |
| 1.0.0.78 | 546 | 7/22/2025 |
| 1.0.0.77 | 551 | 7/22/2025 |
| 1.0.0.76 | 180 | 7/7/2025 |
| 1.0.0.75 | 169 | 7/7/2025 |
| 1.0.0.74 | 103 | 7/5/2025 |
| 1.0.0.73 | 277 | 5/12/2025 |
| 1.0.0.72 | 233 | 5/11/2025 |
| 1.0.0.71 | 218 | 5/11/2025 |
| 1.0.0.70 | 126 | 5/9/2025 |
| 1.0.0.69 | 121 | 5/9/2025 |
| 1.0.0.68 | 120 | 5/9/2025 |
| 1.0.0.67 | 130 | 5/9/2025 |
| 1.0.0.66 | 132 | 5/9/2025 |
| 1.0.0.65 | 133 | 5/9/2025 |
| 1.0.0.64 | 167 | 5/9/2025 |
| 1.0.0.63 | 177 | 5/9/2025 |
| 1.0.0.62 | 187 | 4/30/2025 |
| 1.0.0.61 | 182 | 4/30/2025 |
| 1.0.0.60 | 180 | 4/30/2025 |
| 1.0.0.57 | 180 | 4/30/2025 |
Version 2.0.0: Full xUnit v3 compatibility, fluent API, enhanced performance metrics, and production-ready features. See CHANGELOG.md for complete details.