Rate Limiting a .NET Core Application using the Sliding Window Algorithm — Part 2, Validation with Unit Tests

In the first article of this series, we implemented a rate limiter in .NET Core using the sliding window algorithm with the intention that it will be used by an application to self-limit its requests to an API. Before we can put our implementation into practice, we first need to validate that it functions as intended.

In this article, we will discuss how to validate our rate limiter with unit tests. We will first setup the test class and several helper methods that can be reused throughout our unit tests. We will then turn our attention towards the unit tests, examining each based on the scenario that they assess; this is done similarly to how we implemented the rate limiter in the previous article. The unit tests will be written using xUnit and Moq.

Setting up the Test Class

Setup of our test class, is relatively simple, requiring only a single mock implementation and three helper methods. The setup for the class is displayed below,

As you can see from the Github gist, we need only mock a single implementation: the interface. We mock so that we can precisely control what values of the time are returned from calls to made by the class (as was alluded to in the previous article).

We have added several helper methods to our test class to aid with code reuse,

  • as the name suggests, converts milliseconds to time in ticks
  • calculates how much time must elapse for a request to conform given the rate limit and the current request count*
  • saturates the object by requests, mocking the time returned by the implementation with a value for the time elapsed that starts with and increments by for every iteration. The method assumes that each request conforms up to the limit and asserts this using the test framework

A rudimentary unit test is also included in the code excerpt above; its responsibility is to verify that the class is performing validation on its constructor inputs.

*The equation for is derived from the equation for calculating the approximate request count by assuming the previously tracked window reached the rate limit and then solving for the elapsed time.

Unit Tests by Scenario

Our unit tests can be divided into three scenarios — each assessing how the implementation responds under certain conditions:

  • When an incoming request is received, but there is no previous window
  • When an incoming request is received, and there is a previous window
  • When the time between the incoming request and the start of the former, current window meets or exceeds two or more window lengths

Scenario #1: A previous window does not exist when checking the conformity of an incoming request

As the title states, we need to assess how the rate limiter responds when it is in a state where there is not a previous window. This can occur in two situations,

  • The current window being tracked is the first seen by the rate limiter
  • There has been a significant amount time between the start of the former, current window and the incoming request (seen in the last scenario)

We will address the former situation in this sub-section and save the latter for the third. When this situation occurs, we expect the rate limiter to allow all requests up to the request limit (i.e. if the request limit is 50, then 50 requests will conform/be allowed). The unit test for this scenario is displayed below,

The unit test above is rather simple,

  • We create a instance passing in arbitrary values for the rate limit and our mock implementation
  • We saturate the window with as many requests as the value of via a call to — which also acts to verify that each request up to the limit is considered to be conforming by the rate limiter

Notice that we provide for the input value of . We do this because the elapsed time would have no impact: a previous window cannot be used to calculate the weighted request count and the request limit for the current window has not been met.

Scenario #2: A previous window exists when checking the conformity of an incoming request

The second scenario is somewhat more involved than the previous. In this scenario, the rate limiter has a previous window to reference so we expect it to impact checks to the conformity of incoming requests. We can assess our rate limiter’s response in this scenario through the following unit test,

The unit test above is certainly a larger block of code, but is no more complex than the first,

  • We create an instance of and saturate the initial window, assigning to the return of
  • At this point, the value of places us at the start of the next window, so we calculate the minimum time that needs to elapse before the next request will conform
  • We setup the next call to to return a timestamp that is only half of the minimum elapsed time calculated in the previous step and then assert that the next request does not conform
  • We do the same thing as the previous step, but setup to return a timestamp that now equals the minimum elapsed and assert that the next request does conform
  • Lastly, we verify that the next request does not conform since we have not advanced the time returned

You may have noticed that we have setup this unit test as a theory. This is not done to test equivalence classes per say, but to give some validation to our calculation of the minimum elapsed time with varying values for the rate limit.

Scenario #3: The timestamp of the incoming request exceeds the current window by two window lengths

The third and final scenario verifies that an incoming request is unencumbered by the former, current window when there has been a significant amount of time between the start of the window and the incoming request (two window lengths to be exact). The corresponding unit test is rather simple, constructed similarly to the unit test of the first scenario:

To validate the rate limiter for this scenario, we take the following steps in our unit test above,

  • Create a instance and saturate the initial window, assigning to the return value of
  • Increment the the value for by two window lengths
  • Saturate the window again, using the value of as the starting value returned by and verifying that the new, current window can be saturated with as many requests as the value of

Running the Tests

From the image below, you can see that our unit tests run successfully — or, at least on my local they do 😜.

An image displaying the rate limiter unit tests running successfully
An image displaying the rate limiter unit tests running successfully

This concludes part two of four in the series 😌. I hope this series of articles has been helpful to you. As I have said in the previous article, if you have any feedback — please, let me know!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store