Following this trail was not easy, or particularly pleasant. I used a debugger to do it. Past experiences have left me gun-shy when it comes to debuggers; generally when I find myself contemplating using the debugger, I start questioning my recent life choices. Nonetheless they are sometimes a necessary evil.

Using the debugger meant first ensuring that I could catch the software at just the right moment to examine the values in question. Fortunately I was dealing with a deterministically failing test, not an occasional runtime error. Even more fortunately I was using RubyMine, which does a pretty decent job of taking the pain out of debugging in Ruby, even when working with the split client/server processes of a Capybara testing session.

Which is not to say that the debugging process was enjoyable. Thankfully this problem could be traced at a single point in execution, and didn’t require stepping. But I still had to methodically evaluate each predicate in turn to see which one(s) were returning unexpected values. In case you weren’t keeping count, that’s eight different methods.

Evaluate episode.available_to_user?. Click through to the method definition. Evaluate free?. Evaluate user.current_subscriber?. Click through to the method definition. Rinse. Repeat.

I could have done this without a debugger, of course. I could have annotated the methods with good-ole’ print statements instead. This is always a little tricky with query methods, because you have to make sure to preserve the method’s return value. One annotated method might have looked something like this:

class Episode # ... def available_to_user?(user) result = (free = free?) || (current_subscriber = user.current_subscriber?) puts "episode.free? == #{free}" puts "user.current_subscriber? == #{current_subscriber}" result end # ... end

This is a huge alteration to the method. If I had annotated seven more methods like this, I would have had a complete picture of why that boolean return value was false. Of course, I probably also would have had a screen full of output, since the method is probably called more than once in a single test, and I would have had to figure out which output corresponded to the point in the test I actually cared about…