Scalable and Performant ASP.NET Core Web APIs: Load Testing

Load Testing

This is another post in a series of articles on creating performant and scalable web APIs using ASP.NET core 2.0. In this post we’ll focus on tools that can help us load test our API to ensure it’s going to perform and scale when it goes into production. Performance and scalability issues are much easier and quicker to resolve before our API has gone into production, so, it’s worth testing our API under simulated demand before it gets there.

WebSurge

WebSurge is a load testing tool for APIs behind the firewall or in the cloud that is really simple and quick to use.

During development, when we think our API is nearly complete, we can use WebSurge to determine how many requests per second our API can handle. So, this is not testing specific user scenarios under load, this is just testing a single API end point under load to see how many requests per second it can handle.

WebSurge

This video gives a great overview on WebSurge.

We’ll use WebSurge frequently during this series of blogs to determine the improvement we can make by doing different things.

Visual Studio Load Testing

If we are lucky enough to have the Enterprise version of Visual Studio, we can use the load testing tool within Visual Studio. It is more flexible than WebSurge but it is more time consuming to write the tests. This is perhaps a good choice if we are writing tests that simulate specific user scenarios under load.

In order to create tests, we first add a “Web Performance and Load Test” project to our solution.

When producing the actual tests we could record the test using a IE plugin that integrates with Visual Studio’s load testing tool. This is great for load testing traditional server driven web applications. However, for testing web APIs, creating a c# unit test is much simpler and gives us a lot of flexibility.

Unit Test

Now that we have a test, we can put this under load. We do this by right clicking on the project and clicking “Add > Load Test …”. A wizard will help us create the load test. We will see that we can use a cloud based load or a load generated from our PC (and potentially other PCs in our infrastructure). The wizard lets us configure lots of stuff such as the test duration, the think times (if we are trying to simulate realistic workloads), the number of users and obviously the tests to run and how they are mixed up during the load. After we have completed the wizard, we can run the load test by clicking the “Run Load Test” icon.

Run Load Test

After the load test has finished running, we get a summary of the results. The statistic that I’m most interested in is “Tests / Sec” which is similar to “Requests / Sec” in WebSurge.

Load Test Results

BenchmarkDotNet

BenchmarkDotNet is a low level tool to help us understand how our code will run at scale. It’s particularly good at comparing different pieces of code that give the same result to determine which one is the most efficient.

To do this we need to put our code in a .NET Core console app and bring in the BenchmarkDotNet nuget package.

As an example, let’s test the fastest method of iterating through a list to find an item. We’ll compare a classic for loop, a foreach loop and a FirstOrDefault() LINQ statement.

The functions to be tested need to have the [Benchmark] attribute. We simply call BenchmarkRunner.Run() in Main to invoke the test.

To simulate what would happen in production, we should build the console app in “release” mode and call it from the command line:

Here’s the results:

… the classic way is always best!

So, now we’ve got the tools we need to test and profile our ASP.NET Core Web API. In the next post we’ll get into the actual code, starting with our data access code.

Don’t forget to subscribe to the rest of this series in the footer of this page!

Share this:Share on RedditTweet about this on TwitterShare on LinkedInShare on Google+

4 Comments

  1. Hi,

    I’m aware the main subject of the post is not the benchmark itself, but you can use *List.Find* which uses a for loop. So, the performance are equivalent to the ClassicFor but much simplier to use :

    [Benchmark]
    public Person ListFind() => people.Find(p => p.Name == find);

    Method | Mean | Error | StdDev |
    ———– |———-:|———-:|———-:|
    ClassicFor | 2.916 us | 0.0181 us | 0.0170 us |
    ListFind | 3.177 us | 0.0080 us | 0.0075 us |
    ForEach | 5.322 us | 0.0334 us | 0.0296 us |
    Linq | 10.623 us | 0.0553 us | 0.0490 us |

  2. Neat post. Thank you for putting this together. I’m super curious about one thing. For Visual Studio Load Testing, have you taken a look at web performance tests? They shouldn’t require any code for a basic test like the one demonstrated in the blog post.

    1. Thanks Corey – good point! I’ve used web performance tests in the past to test the performance of ASP.NET web forms or MVC web apps but struggled with it when testing a real web API. Simple GET requests (like the one in my example) are fine, I struggled with POSTs PUTs & DELETEs and chaining requests together with variables in URL parameters, request bodies and HTTP headers ….

Leave a Reply

Your email address will not be published. Required fields are marked *