We've looked at benchmarking apps in the past using Benchmark .NET, but that requires a reasonable amount of effort if you wish to perform load tests geared up for, for example, a web application. Enter NBomber: a "modern and flexible load testing framework for Pull and Push scenarios, designed to test any system regardless a protocol (HTTP/WebSockets/AMQP etc) or a semantic model (Pull/Push).".
It comes with several plug-in packages which can be used for specific use cases e.g. HTTP traffic etc. and comes with built-in report generation as well as a flexible sink abstraction to write out to whichever visualisation / logging store you want.
Writing a load test with NBomber
Here's a basic test with NBomber that hits an HTTP endpoint (I'm using the "basic" NBomber setup rather than the HTTP Plugin which has more features). Remember, you can use NBomber for anything you want to load test, it doesn't have to be HTTP (although that's a common use case). You start by creating a step - a single test element:
open NBomber.FSharp
open NBomber.Contracts
open System.Net.Http
let client = new HttpClient()
let step =
Step.create (
"My First Test",
fun ctx ->
task {
let! response = client.GetAsync "http://localhost:5000/"
let! content = response.Content.ReadAsByteArrayAsync()
return
if response.IsSuccessStatusCode then
Response.ok (content, int response.StatusCode)
else
Response.fail (response.ReasonPhrase, int response.StatusCode, content.Length)
}
)
Inside your step body, you can run any code you like - in this case, I'm just hitting an endpoint and returning success or fail as required.
Once you have your steps, you compose them into scenarios. Here, I've only got a single step but you can have as many as you like:
open NBomber
Scenario.create "My First Scenario" [ step ]
|> Scenario.withoutWarmUp
|> Scenario.withLoadSimulations [ InjectPerSecRandom(100, 500, seconds 20) ]
|> NBomberRunner.registerScenario
|> NBomberRunner.run
The test above will run my Step between 100 and 500 times a second, for 20 seconds, with no warmup.
This test can be run from a standard console app. Once your test has run, you'll receive results similar to below:
┌────────────────────┬──────────────────────────────────────────────────────────────────┐
│ step │ ok stats │
├────────────────────┼──────────────────────────────────────────────────────────────────┤
│ name │ step │
│ request count │ all = 6325, ok = 6325, RPS = 316.2 │
│ latency │ min = 0.12, mean = 6.01, max = 211.13, StdDev = 21.2 │
│ latency percentile │ 50% = 0.34, 75% = 2.17, 95% = 24.99, 99% = 143.1 │
│ data transfer │ min = 0.012 KB, mean = 0.012 KB, max = 0.012 KB, all = 0.0724 MB │
└────────────────────┴──────────────────────────────────────────────────────────────────┘
status codes for scenario: scenario
┌─────────────┬───────┬─────────┐
│ status code │ count │ message │
├─────────────┼───────┼─────────┤
│ 200 │ 6325 │ │
└─────────────┴───────┴─────────┘
All results are also automatically logged in CSV, Markdown and HTML formats as well.
You can find a basic sample app and testrig that was used for the code samples above here. It uses Paket for dependency management, but there's no requirement for that - I actually only used that here because earlier versions of NBomber had a dependency graph that would under certain circumstances fail. I'm glad to see that in the new v3 of NBomber, this issue looks to have been resolved.
Summary
If you're writing applications that require some form of load testing, NBomber is a tool that you should have a look at. It has a growing set of plugins and extensions, and has a number of commercially-supported options with features including clustering support.