Recently I was asked to implement a new feature for a service. Basically, a simple Circuit Breaker pattern to throttle users in case of abuse. The request was worded like this:
"We set a cap for how many messages we expect to receive in a timeframe: say in a 1 minute period, we expect no more than 4 messages. As messages pour in from a spammer, we might process the first 4 messages in 1 minute, but once we exceed 4, we bail and call it 'spam'. We could adjust the timeframe and message cap as we see fit. We could also add logic to track numbers that we detect might be spammers, and completely block repeat offenders.
That gives me a simple understanding.
Let’s convert that feature into a feature test:
I don’t have any code for this feature yet, but I can wager what I’ll need: a SpamCircuitBreaker (the thing I’m creating), and three fields: Enabled, Time Limit and Cap.
When I validate, I want to validate that the user who is spamming has been throttled, but also want to ensure that the non-abusing user is not throttled.
Side note: I also don’t want my SpamCircuitBreaker on all the time during testing, so my first step is “Given I have turned on the SpamCircuitBreaker” so that I can give myself an escape valve. It will be turned off by default, except for this test and in Production.
Now time to implement the test. If I run it right now, it’s red. Let’s make it green!
You might notice that the “sent the following messages” step isn’t shown below, that’s because it already exists – thanks code reuse! Reusing test steps is one of my favorite things about Specflow – it lends itself really well to reuse.
So lets start implementing the steps that are new:
Now I have the steps that I need to implement, let’s implement them!
I’ve decided that I want to configure the SpamCircuitBreaker in a global way… and I only want it enabled in Production, or for this specific test. I don’t’ want it on for other tests, as it could interfere. I could do this a number of ways, but I have a framework in my AppContainer that will work nicely, so I’ll put the SpamCircuitBreaker there.
Here are my steps after partial implementation:
Now, lets get to implementing our code!
Create a stub for now:
Give AppContainer the property:
Now, we can implement what we need from our tests. I use Resharper, so I press Alt+Enter to implement all of the properties I accessed from my tests automatically. This “fixes the red” shown in the test shown above, or as I prefer to call it “Fix the hate!”
And boom! The class is created:
See how our tests “drove” our development? Cool huh?!
Ok, run the test!
It’s red. We need logic! Let’s get to it. The thing that will use my SpamCircuitBreaker is the “Processor”, so let’s add logic there.
I started with:
and I added:
Now, lets implement CheckIfThrottled:
Now let’s run the test.
It failed! Looks like I forgot to actually process the messages, whoops. I’ll add a line in the step that should process:
Now the test passes!
First, I’m going to clean up my logic.
I see some repetition in my code, this pattern is subpar:
I think I can move the first “IsThrottled” check inside the “CheckIfLimitIsReached”, and this should clean it up a bit:
There, that looks better!
Now I want to refactor to interface this class out and use it in dependency injection. I attached it as a property to my AppContainer for ease, but now I think there’s no reason it shouldn’t be injected normally. Time to remove that wacky bit.
I’ll use resharper to extract my interface. “All public properties” should do:
(I use GhotDoc to help me auto-document, too)
There, now I have an interface! I’ll place this in another file and I can set up the injection.
Now, I’ve decided that my test should also make sure that ThrottledNumbers actually expire. So I’m gonna write a second test.
Here’s the second test:
I run it, and it fails! Oh no! Good thing I caught that bug!
So the number was still there, damn!
Let’s look at the code and figure out why:
Ahh, here’s the problem. I’m checking if isThrottled and cleaning the cache AFTER, then returning the result. Oops!
Let’s change the order of operations so that we clean the cache first. While we’re at it, let’s also add an object lock so the class supports threading.
Rerun our test:
And that’s how you Red-Green-Refactor!