As I said earlier, I wanted to talk about other aspects of an event described by Matt Terry about us fixing a bug using a test first mentality.
If you look closely at his retrospective, you’ll notice a problem. Let me give you a hint. Look at the phrase, “The last thing we did was wire up our new methods in the appropriate places.” Got it? NO? Consider how that fits in temporally (time-wise) with the declaration that, “As you’d imagine, now our tests are passing.”
If you’re looking closely, we were actually writing tests against the wrong class. If we had written the tests against the right class, then there would be no need to wire things up. I didn’t see it either at first.
Enough dancing around the point. Here’s some concrete information. The scheduler uses reminders to schedule when an Android alarm or notification needs to occur. There are a few different types of reminders and that’s only important because I use a factory to build the reminders and their sub structures.
Where are the tests? I was testing the factory’s ability to build the correct structure, so that’s where I naturally started adding tests. I ended up duplicating logic for the scheduler in these tests. This turned out to be a problem in an earlier feature when I changed how the reminders were used and didn’t add them to the scheduler until I started system testing. It follows then that I should bring the scheduler in to the tests. That way when I change the way the reminders need to be used, the scheduler will change along side. And we won’t have an, “Awe, crap,” moment where we realized the reason it’s not working is because we only half solved it.
Why didn’t I test the scheduler? Because it was coupled to three things. I used the Android Alarm Manager, the Android Notification Manager, and time. Yes, Virginia, you can be coupled to time. I can uncouple the time at this level the same way I did at the reminder level: pass it in as a method parameter. But where does this stop? In my case I would choose to new up a GregorianCalendar at the Android Activity level. This is also where I would wrap the AlarmManager and NotificationManager so I can pass in test-doubles.
What’s the theory? It’s an application of the Ports and Adapters architecture. Ports and Adapters, also called “Hexagonal Architecture” can be applied in a fractal manner. It is a pattern of separating input and output from a business object. In our case the input is time and the outputs are the Alarms and Notifications. By thinking about this input / output metaphor I can pass in a time and wrappers for Alarms and Notifications that can tell the tests what values the scheduler outputs. Testing made easy! Okay. Easier.
Or, if I want to be cheap about it, I can wrap all three in protected methods and test a subclass of the scheduler that allows for injecting time and record when an alarm is to fire and what notification was displayed. There are a few reasons that this isn’t the best idea, but it is the quickest way to get this huge chunk of code under unit test and not rely on manual or system tests to ensure this code works.