Building Blocks
As I mentioned in my Getting Testy series, I typically practice a form of outside-in testing, or as Justin Searls has been calling it, Discovery Testing.
Often, I end up with small, simple, reusable classes that I can use as building blocks.
I recently worked on an iOS project in Swift where some of these building blocks emerged in the course of test-driving the application.
Periodic Tasks
One feature of our application needed to repeatedly run a task at some interval. This can be done with NSTimer
, but we wanted a simpler interface in order to make testing easier. We used our tests to derive a PeriodicTaskLauncher
protocol with the interface we wanted.
We also implemented a FakePeriodicTaskLauncher
test double for testing. Eli Perkins has a very nice introduction to this technique.
Once we finished developing the code that uses PeriodicTaskLauncher
, we were able to move one level in and test-drive the default task launcher implementation.
Rate Limiting
Later in the same application, we had a situation where we needed to perform a task in response to user interaction, but we needed to limit how often we’d perform the task. To solve this problem, we built a RateLimiter
class.
RateLimiter
starts in a quiescent state. In that state, it executes any incoming action immediately and then starts its task launcher. In that state, it hangs onto any incoming actions. If more actions come in before the task launcher runs, the previous action is discarded and the new action is remembered. When the task launcher runs, it executes the remembered action. If there is no action to execute, the task launcher is stopped, putting the RateLimiter
back into the quiescent state.
We use RateLimiter
like this:
Note that RateLimiter
makes use of PeriodicTaskLauncher
internally.
Conclusion
When I practice outside-in testing, I often end up with these small, simple, single-purpose classes that can then be used in other contexts. They form reusable building blocks that can be used throughout the application very easily.
I find that this testing discipline naturally results in classes that follow the Single Responsibility Principle and are naturally reusable in other contexts as with the PeriodicTaskLauncher
here.