Browsing the blog archives for April, 2012.

There’s No Time for That! – Testing the Untestable

work safe

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.

No Comments

We’re Besties! – Pair Programming

work safe

The lead on my most recent project, Matt Terry, wrote a blog post on test first this Saturday that tweaked a few thoughts in my head. There is so much to unpack in that particular event he described, so I’m going to write a couple of blog posts and try to give a few different angles their due.

Now about the story he told. It is true, but …. let’s say it diminishes his role in the situation. I’m going to see if I can’t sing the praises of pair programming.

We found a tricky bug in our scheduler. Matt came over to my desk and we decided to pair on the problem. We started by trying to understand the problem. We chatted over the scenario It was essentially a time race condition. Different components need to know new information at different times. When can we inform these serialized and stored objects the world changed? We wrote some notes and modeled a scenario. Then we wrote some tests. We constantly checked in with each other that our assumptions were correct. We made a point to talk out loud about every thought I was thinking as we coded. We made a point to answer and ask questions. Matt was also great at guilting me in to correct action. There wasn’t a thing he said or did, and he’ll probably be a bit mortified to hear about it. He just had to be in earshot when I muttered, “but that should probably be it’s own method,” and then I’d HAVE to fix the code because the alternative is like walking past trash and NOT picking it up. While someone watches you. Social pressures can be a wonderful thing.

The point is, by being there talking through the whole process we were able to quickly understand the problem and work through the solution. We fixed this far more quickly that either one of us could have on our own. In addition, the solution was better because I wasn’t throwing it over the wall to be reviewed. We wrote the best code we could because we knew we were watching.

All in all, I’m looking forward to pairing with him again. I’m bad at it, but it’s a skill and one I’m starting to find more and more enjoyable as I get better and better at it. Believe it or not, development is an inherently social event and pair programming brings this to the forefront. It increases communication by making it visibly essential to everything.

No Comments

Usul’s Words Have Power, Part 2: Use Cases

work safe

In the near future I’m going to use a word. I’m going to use it as a cornerstone of an argument for something a bit radical. Not very, but a bit. The word is Use Case. Since everyone has an opinion, I’m going to be specific about what I mean.

Use Cases were invented by Ivar Jacobson in the 1980’s as a way to solicit requirements. It is NOT a way to use spare day-old boxes, arrows, and lollipops. While UML has a thing called Use Cases, that ain’t it. I mean a list of actor/system interactions with some context and some deviations. What do I want to use it for? It gives me, a developer, a fuller idea of what I’m actually supposed to build for my customer.

As close as I usually get is, after a conversation with a customer or team lead, is a bulleted list of things to do. It looks a bit like this:

  • Add an activity to my time sheet.
  • Set a start and end time to the activity.
  • Describe the activity.
  • You can’t save the activity if it doesn’t have a description or time range.
  • Choose a time code associated with the activity. There should be a default.
  • Display the activity on the time sheet.

It is generally how I think I’m going to implement the feature in enough detail to get all the corner cases implemented. This is not a Use Case, it’s a todo list. It doesn’t share any information about workflow or context or motivation. In short, I’m left with no empathy for my users. And if I don’t have empathy for my users, how can I try to make their lives, or at least their experience using my software, better?

A Use Case as I want to use term focuses on workflow and on the context surrounding the actions being described. It has the following parts:

  • Title A name to distinguish it from other Use Cases
  • User Intention Describes who, why, and what
  • Precondition Where in the system your interaction will take place
  • Interactions The happy path
  • Extensions How conditions cause the happy path to deviate
  • Postconditions How I expect those interactions to impact the system

Most of these are just prose, the interactions and extensions are tables with two columns, the actor’s actions and the system’s response. Each row is numbered. The Interaction rows are simple numbers. The extensions are numbered with numbers and letters to relate to the interaction they modify.

Great! I’ve created a wonky definition! Now what the heck does that look like?

An example

Add an Activity to a Time Sheet

Tony logs time spent working on the Bryce Co project so Cheryl can bill against the Bryce Co account.

Precondition User is logged in and viewing an editable timesheet

Interactions

User Action System Response
1 User clicks on the new entry button System presents the user with the action entry dialog.
The user’s most recently selected time code is automatically selected
Only time codes the user is authorized to use should be displayed.
Focus is on the date field
2 User enters a date
3 User enters a start time and end time
4 User describes the activity System will enable the ok button
5 User changes the time code
6 User clicks the ok button System removes the dialog
System displays the new record in the time sheet and saves the updated timesheet.

Extensions

User enters none or only one of the start and end time

User Action System Response
2a User enters a date outside the range of the timesheet’s date range System will post a message displaying the valid range and empty the date field
3a System will not enable the ok button until a valid start and end time are present
3b User enters a start time after the end time System will move the end time to 15 minutes after the start time
3c User enters an end time after the start time System will move the start time to 15 minutes before the end time
4a User doesn’t enter a description System will not enable the ok button without a description
5a User clicks on the cancel button System removes the dialog and discards the entry
5b System is unable to save the entry then a message will be displayed and the dialog will not be removed unless cancel is selected
No Comments