A common TDD concept is that you write tests targeting the most optimal API imaginable, rather than contorting your code around current production realities. It’s possibly the most practical form of flight of fancy anyone has ever considered. Run free in a field with your API before you build retaining walls to thwart mudslides. The resulting code is much better because you work toward the best possible experience, deferring details for as long as possible. It’s amazing how well the process works in all types of contexts.
Let’s consider the Rails test suite. What’s our flight of fancy? Super duper fast tests. Once we set our sights on lightning fast tests, we bring them to life, nevermore accepting pokiness as threadbare reality. Thankfully many smart people have done great work to bring this closer to being, but not without a touch of controversy.
Background on Test Performance
Executing a single (trivial) RSpec test against an empty Rails app is on the order of several seconds. This performance is not the product of anyone’s fantastical dreams, of course, but a function of reality. The Rails stack takes time to load and there’s no obvious way around it. Except, of course, to go around it. All of the sudden tests are several orders of magnitude faster. That’s the trick. Only use Rails when necessary and life is beautiful. Which begs the question, when exactly is Rails necessary in a Rails app?
Rails as Detail of Project
DHH chuckles at the notion of Rails-as-detail: "… Web apps don’t wake up in the morning and don an iOS suit." And it's true, no Rails codebase will ever end up running on an iPad mini. That said, how many people would benefit from lopping off functionality into lightweight independent services but have little idea how to begin? Generally speaking, who doesn’t want a better understanding of the entanglements of an application? After all, clear definition of dependencies is a central challenge in programming—it's a core tenet of useable design.
Quick Example of Rails as Detail
Take a module that lives in Rails whose mission in life is to talk to a third party API. I recently set out to extract something like this from Rails. It required one piece of Rails (the from_xml method) to parse the response:
response = Hash.from_xml(http_response)
So I added the single dependency to the api spec (and learned a bit of Rails trivia in the process):
In this simple but common case, the entire Rails stack is obviously not needed to test the wrapper. By decoupling from the entire framework testing is quick and we’ve outlined the specific dependency for the benefit of posterity.
Controversies be Darned
It's true. As programmers we should be happy about and embrace progress in the form of:
1. Orders of magnitude performance improvements. Goes without saying. Orders of magnitude efficiencies are ultimately what we’re concerned with in various forms every day. Does it take 10 people to ship an order or just 1? Does it take 500 ms to execute a test class or 5 minutes?
2. Clearer dependencies. Asking classes to outline their dependencies to Rails and elsewhere leads to clarity. Rails is a big framework. Seeing the forest for the trees drives a better understanding of the framework and its relationship to the domain logic.
tl;dr Actors in Today’s Flight of Fancy:
Rails = Chuck wagon with all manner of useful provisions, but heavy.
Fast tests = Beautiful pastoral field, but far away.
Independence = Awesome horse that gives you a ride and teaches you lessons along the way.
Take the ride, enjoy the field, pick some flowers.