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.
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.
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 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.
RateLimiter like this:
RateLimiter makes use of
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