Paul Hammant's Blog: TDD When You Can't Refactor
David Heinemeier Hansson (DHH) recently claimed Test Driven Development (TDD) is dead. Many have responded including ThoughtWorkers, saying it ain’t so. I think there’s a link to refactoring, or more accurately refactor-ability for the technology in question.
Rails is a 4GL
DHH’s Rails framework is very advanced. I once heard that it was more like a 4GL, while Ruby itself is a 3GL. Of course you’d have to be old enough to know what the categorization meant as the industry pushed towards genuine 4GLs that were positioned as panaceas. Back in the 90’s I mean, when “Make a cup of tea” was a way of quickly giving an example of what 4GLs would do (when they arrived). Anyway 4GLs came and went by the end of the 90’s, thank goodness. The Rails == 4GL statement in 2005, however, struck a chord.
A confession (digression)
For me Rails was a missed boat. Dan North introduced me to DHH at JAOO in 2003. Or DHH found me. He demoed an early version of Rails. I was the PicoContainer/Java guy, and maybe I could have helped him back then. Of course, I didn’t really get the full value proposition, and politely suggested it wouldn’t scale in the enterprise (or some other falsehood), and moved on. I made a mental note to pursue DHH as a ThoughtWorks hire after the conference, but never followed up. As importantly I didn’t follow up on a gut feeling that I should dig more into what this kid showed me. No matter, Obie Fernandez caught my dropped ball and by the end of 2004, ThoughtWorks was repositioning its developer base to be able to pioneer Rails development.
The link to refactoring (from the article title)
Maybe Rails apps are too sophisticated in terms of coding constructs to be easily and cheaply test driven. At least compared to Java. Maybe frameworks are harder to TDD too, compared to libraries. Remember a library is typically controlled by your code, and has no side effects of invocation (no threads, reentrant, etc). A framework typically controls your code. Indeed a framework often instantiates your app classes and chooses it’s own moments to invoke methods. Lots of magic there, and the ‘seams’ that we look for to make smooth tests, are often missing. Some frameworks like the early versions of .Net’s ASP.Net were notoriously hard to test, let alone test-drive. Generally speaking, nobody would want more code just to be testable, and Rails in 2004 was certainly a lot less code for the same thing.
Refactoring, in tools like Intellij for Java, is like giving the Mona-Lisa a proper smile with your fingers, hundreds of years after the last brush stroke, and perfectly. Not only that, but Intellij’s local-history undo is perfect too. Don’t like that smile? Hit Ctrl-Z.
Can’t Refactor means Won’t Refactor
I’ll claim that TDD with no cost of creation or maintenance, requires hard-core refactoring support in the IDE. Anyone using an IDE (or editor) that doesn’t have hard-core refactoring support for the language in question is going to have second class TDD.
Anyone in love with their editor IDE that doesn’t have hard-core refactoring support, is going to be Basian correlated with “I think TDD is overrated” because it fits their tool choice. In short, if you love an IDE that can’t refactor, your confirmation bias means you’re likely to think the things refactoring facilitates like TDD, are not worth it.
Full disclosure: I don’t know what IDE DHH uses.
Your confirmation bias…
… means you’re likely to think the things refactoring facilitates like TDD, are not worth it</div>
JetBrains, AngularJS and refactoring
Granted, this is a mild segue from the “can’t refactor won’t refactor” central position of this article, but it is related.
JetBrains released RubyMine 1.0 in April 2009, but a public preview was available from November 2008. That’s when refactoring became a casual possibility for Rails apps - up to a certain point, and only if you used RubyMine (vs your fave IDE/editor) of course. How far RubyMine’s refactoring capability goes today, for Rails apps, is a different thing.
Angular in 2012/13 is the industry darling that Rails was in 2006/7. Not just in my opinion. It is further “less code” proposition again, and deserves the patronage it has.
In 2009, I chuckled at (Googler) Miško’s description of his new side-project - AngularJS. Breakfast before a GoogleWave hackathon with Ola Bini, a day after Wave’s announcement, wasn’t the right moment to see code, so we moved on, but Miško was a little butt-hurt. I knew I wasn’t respectful, and had an appropriate pang of guilt/shame there and then. That gut feeling was back - that I should have a second look. Still smarting from being the ThoughtWorker that dropped the Rails ball, I thought I’d not make the same mistake twice - I dug deeper a few days later - and “stroke of genius” was my lasting feeling. Soon after that I did my first Angular blog entry. I think Miško has forgotten that I chuckled at his brain-child as a first reaction, and that is one of the hallmarks of his personality, that I’ve seen in some other super-achievers.
Anyway, JetBrain’s pushed AngularJS capability into their suite of products. Refactoring of Angular specific things is a reality now - yay!
Inlining of JS function into an Angular attribute
If you select a controller-function reference in an HTML page in WebStorm, you can see a refactoring menu (the Angular example from todomvc.com):
Choosing inline, would give this resulting code:
For some reason Ctrl-Z is not perfectly hooked up yet (local history), but source-control always allows a timely revert, as it should.
I’m quite pleased with this as an early refactoring achievement for WebStorm (and I presume all the other other JetBrains IDEs in turn), but what I really want is “extract/inline directive”, “extract/inline service” and other Angular idioms. I have no doubt that JetBrains will get there in time.
Where to put JavaScript in Angular projects?
As we saw with …
<button class="destroy" ng-click="todos.splice(todos.indexOf(todo), 1);"></button>
… we effectively have JavaScript in Angular’s attributes. In this case, it’s too much, but simpler expressions are perfectly OK in my opinion. Where is the cutoff point for what you should/shouldn’t do in an expression in a HTML attribute? It matters because it’s code that can’t be tested xUnit style. If it can’t be tested it can’t be part of a TDD build-out. You could always test it in component-centric Selenium-WebDriver stage, but that’s not xUnit style tests. Selenium tests at a tight component level would not have an execution time lower than 1ms, which xUnit tests normally do, even if you could claim you have fuller coverage.
I’m not missing the testing of JavaScript I’m not writing
Although I’m not missing the testing of JavaScript I’m not writing, this fragment was one that should have tests. In time we’ll get test helper software that extracts all the angular expression fragments to a place that could have tests:
$tests.destroy_button.ngClick = function (todos, todo) {
todos.splice(todos.indexOf(todo), 1);
};
// or something similar that would parse w/o error.
If AngularJS (and other frameworks having magic) could generate testable code interpretations of the magic (and manage those perfectly during refactorings) then we might be able to see full-speed TDD for these frameworks.