Skip to main content

Test Driven Development

Recently I watched David Heinemeier Hanson's controversial railsconf keynote and I thought I'd express my views.

His main argument suggests TDD is dead. Oddly I agree with many of his headline views which I try to document below. I just not convinced of his supporting arguments.

DHH appears to have a dim view on TDD practicing developers; whom he generalises write simple unit tests having 100% code coverage, use many mocks, but test nothing real. His example was a bug in Basecamp which lost customer data, their code had 100% code coverage, but still didn't work. I really doubt there are many developers who just stop when they hit 100% unit test coverage?

I'm not going to speak for everyone else; I only started using TDD (not exclusively) about 18 months ago despite 12 years developer experience writing some really cool stuff, some well designed and some not so well...

The well designed stuff has tended to be driven by TDD. I've never used code metrics, I use TDD to help focus my mind on the current thing to write. By factoring a problem down into smaller chunks I can focus very well and avoid 'coders block'. I've never approached factoring from a test perspective, I've tended to think how the architecture should be sculpted and broken down. I can usually find a bite-size component to write a test for and implement, it also ensures you can optimally test many permutations of a system using more than 1 unit. More importantly, the tests I write provide a specification for what I expect that thing to do.

This practice has a danger that you might forget what you originally started to break down - 'the story'. I'm not going to pretend I've never had 100% tests pass and no working system, in fact recently I wrote an email sending service, but forgot to include a real template so the email would be sent to the correct people, but with no body (I'd mocked the template in a test and asserted the correct data was received), this system was never deployed but if I'd concentrated solely from a point of unit tests it could have been. In this and other cases you still need a guiding light to ensure you don't write a suite of insufficient tests or even direct development in the wrong direction. In this example an integration test using the real template was sufficient. This provided me with confidence the the units were being invoked correctly and if the template were to be broken in the future I am confident the test would fail.

Where I work we use integration tests to ensure the services use unit tested modules correctly and ultimately acceptance testing to ensure things do work as the user expected using real databases and real services; only in very rare cases components are faked for acceptance testing if absolutely required (say to avoid sending real emails).

I like decoupled unit tests with an encompassing system level test. Testing only at integration level would lead to far too many permutations of code paths which could lead to poorer test quality.

I am also happy that acceptance tests take a while to run too, with automated tools like Selenium you can perform this within a few minutes before a push. The integration tests run in less than 10 seconds and the unit tests are pretty fast.

A big danger when using mocks during testing is where your assumptions have changed, say a field is removed or renamed in a web service or database response. If this data has been stubbed in your test, your test will always pass despite never being able to work for real. These types of problems I'd never really encountered before using TDD. It makes code reversibility difficult as the production code migrates to a new style of architecture the tests might not be properly aligned meaning problems occur in user testing that really should have been caught during the code redevelopment.

I admit in a few cases I've wondered why some unit tests run slowly and I will investigate the production code to see if it something that can be optimised. Sometimes something I have no control over but has a known and fixed interface can be safely mocked out. However if it the test is slow because of the production code, I'm not going to cut corners just to make the test suite run faster, that reduces the usefulness of the test. Architecture and production code should become the first priority once the associated tests give a certain level of confidence.

DHH further generalises his view of a developer to one that then 'sprinkles some design patterns' on their code once their tests pass.

I only recently started reading about design patterns and I realised I'd already been using them for many years only now I had a name for the shape of the code and could describe an implementation with a common language.

Toward the end of his talk DHH advocates readability which I wholeheartedly agree with, I really like using functions which read like 'reject where x is y' rather than 'filter x is not y' or 'unless x' in favour of 'if not x'. I'd rather the code explains what it does than have a comment which may not age well.

Finally, the way DHH got a clap for mentioning the delete key is the best way to improve code reminded me of Steve Jobs getting applause for announcing an HDMI cable in an Apple keynote...

I watched this Hangout with interest:

And will be watching the second part too:


Popular posts from this blog

Howto: Ubuntu 8.10, Dell 5530, 3G/WWan and GPS

There is an updated howto on the Dell 5530 using my own python based monitoring tool at


Here is a Howto document for getting the Dell 5530 wwan card working along with the onboard GPS under Ubuntu 8.10. This card does not work conveniently with the bundled Network Manager software, but replacing NetworkManager with other utilities will give the ability to get online using a pretty GUI and not have to resort to using wvdial and the command line...

It should also work with the Ericsson f3507g as I believe the Dell 5530 is a rebadged version of this module.

Check Hardware

First you need to check the modem is detected correctly. On my computer, the modem is detected out of the box by the cdc_acm module and exposes three serial ports (/dev/ttyACM[0,1,2]) for communication. Check by running:

dmesg | grep ACM

Mine shows the following:

[ 34.385302] cdc_acm 1-6:1.1: ttyACM0: USB ACM device
[ 34.390039] cdc_a…

Dell 5530/Ericsson f3507g on Linux

A few months back I posted a howto guide on getting this mobile broadband card working with Linux, what I failed to mention was I continued to work on these scripts. I feel it is appropriate to post a status update on what I use now especially given there appears to be no further updates on the NetworkManager front.

I no longer use UMTSMon as I found it relied on AT+CSQ for the signal quality and during UMTS and HSPA connections, the f3507g/dell 5530 returns either the previous GPRS value or 99,99 which UMTSMon considers as no connection available.

I've used a simplified wvdial.conf scripts and now do the rest of the radio work using python.


# Author: Barry John Williams
# Creative Commons Attribute-Share Alike 2.5 UK:Scotland Licence

[Dialer defaults]
New PPPD = yes
Stupid Mode = 1
Modem Type = ACM Modem
Modem = /dev/ttyACM1
Init1 = AT
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
Init3 = AT+CGDCONT=1,"ip","internet"
Baud = 460800
ISDN = 0
Phone = *99***1#

12 Factor Config for .Net on Mono in a Docker

Recently I've been thinking about how to run a .Net application using Mono inside a Docker, along with this we'd like to use Twelve Factor config.

Whilst it is possible to use some library to access environment variables instead of using appSettings it becomes difficult for database and message broker URLs which other libraries may expect to find within the App.config or Web.config file.

The solution I came up with follows:
Build the application without an App.config moving the existing configuration file renaming it App.config.template. 
Substitute any environment variables you want with ${}.  E.g:
<amqp> <connections> <connection name="rabbit" server="${RABBITMQ_URL}" password="${RABBITMQ_PASSWORD}" /> </connections> </amqp>
Include within the Docker image a script named similar to this and add it to /conf:
#!/bin/shwhile read line doeval echo "$line"done Add your config template to /conf
Then …