Sep 11 2007

A Tricky Trap With Unit Test Mocks in Dynamic Languages

Published by Robert Fischer at 4:05 pm under Uncategorized

Here is another issue that I bumped into with Groovy. For those who haven’t been following along at home, I’m documenting some of these for posterity (see my previous post here). This problem is probably one that can be seen in a lot of different dynamic languages, but I first bumped into it with Groovy-on-Grails, so it gets to be beat up.

Now, one of the tricky parts of any Agile/MVC-based framework is trying to identify how to do unit tests. After all, this is Agile, and that means test-driven development, and it’s hard to do test-driven development when large sections of your code are untestable. But it’s also hard to test Controllers and Views (yes, that’s 2/3rds of the “MVC”), because they’re usually buried deep within dependencies from framework code and user services.

Groovy-on-Grails (like Ruby-on-Rails) will generate entirely new convenience methods at run-time. So, if I have a ConsultingCompany domain class, and it has a Acronym method, Grails will invent a static method called ConsultingCompany.findByAcronym which provides the ConsultingCompany instances that have the given Acronym. You can see more about how Grails does it here.

Unfortunately, to get that method requires firing up the entire Grails reality, which is pretty time-consuming, particularly considering Groovy’s severe performance problems. And since unit testing is the only way of doing type checking, the unit tests get run even more than in a traditional test-driven environment. From personal experience, I’ll tell you that running slow unit tests over and over again gets kind of exhausting, and kills productivity.

The standard solution seems to be to use some dynamic class method injection. In the set-up of your test case, you tell Groovy that the ConsultingCompany domain class now has a method, findByAcronym, and that method will return these certain values. So you’ve basically mocked up the implementation. This is called ExpandoMetaClass (see also the Expando Util write-up). For an example, see the Groovy Users of Minnesota controller testing example over here.

Here’s the problem: once you do that, you’ve now divorced the unit test from the application. We can change the application such that the unit tests pass, but the actual controller code will explode every time.

In our example, all we would have to do is to change the domain class to have an “acro” field instead of an “acronym” field (say, because some abbreviation-loving smartypants just took over). Now the unit tests will still pass, because both the set-up and the controller are expecting findByAcronym, but that method no longer exists — Grails will now create findByAcro.

And, right about this time, you’ll be swearing and/or begging for static typing…

Popularity: 4% [?]

One Response to “A Tricky Trap With Unit Test Mocks in Dynamic Languages”

  1. [...] Any interesting example of this is the particular case of controllers and views in Grails and Rails. Controllers are notoriously difficult to unit test, because they’re riddled with dynamically provided methods. Efforts to solve this via mocking are really treading on dangerous ground. [...]

Trackback URI | Comments RSS

Leave a Reply

Green Web Hosting! This site hosted by DreamHost.