Over a million developers have joined DZone.

TDD When You Can't Refactor

DZone's Guide to

TDD When You Can't Refactor

· DevOps Zone
Free Resource

Download the blueprint that can take a company of any maturity level all the way up to enterprise-scale continuous delivery using a combination of Automic Release Automation, Automic’s 20+ years of business automation experience, and the proven tools and practices the company is already leveraging.

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.

…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.

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) Misko’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 Misko 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 Misko 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.

Download the ‘Practical Blueprint to Continuous Delivery’ to learn how Automic Release Automation can help you begin or continue your company’s digital transformation.


Published at DZone with permission of Paul Hammant, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}