Friday, February 06, 2009

Test-Driven [Design|Development]

Today I learned to appreciate Test-Driven Design a little bit more. Here's the story.

I'm writing some RSS feeds that will contain extensions and other non-RSS elements using XML Namespaces. I'm using Zend_View and Zend_Feed and I thought the best place to put the namespace would of course be at the top of my default.rss.phtml template file - that way I can register all the namespaces at once at the top of the feed. Instead of writing the test first, I wrote the code first. Took maybe 10-20m and seems to work fine:

<rss content="http://purl.org/rss/1.0/modules/content/"
doap="http://usefulinc.com/ns/doap#"
sf="http://sf.net/api/sfelements.rdf#"
foaf="http://xmlns.com/foaf/0.1/"
rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
version="2.0">
....
</rss>

Then I go to write the test. Lo and behold - it's a big pain in the ass to consume the feed using SimpleXML.

It's easy enough to create a SimpleXML element out of the feed, but I can't create SimpleXML elements from the content:encoded XML data:

<content:encoded>
<!--[CDATA[<doap:version>
<doap:name>Project 1.1 - Foobaj</doap:name>
<doap:created>1202221896</doap:created>
<doap:helper>
<foaf:person>
<foaf:name>admin1</foaf:name>
<foaf:homepage resource="http://lcrouch-703.sb.sf.net/users/admin1">
<foaf:mbox_sha1sum>6dd817a0f71590a68131a5e83b1bd73944654e8d</foaf:mbox_sha1sum>
</foaf:Person>
</doap:helper>
<doap:file-release>proj1.file1.tgz</doap:file-release>
<sf:download-count>0</sf:download-count>
</doap:Version>]]-->
</content:encoded>

Because all the namespaces used in the DOAP class aren't in the content. Argh! My first thought is to screw SimpleXML and do a raw string search/parse in the test. But then I had my epiphany: "If I were an actual client of this feed, I would want to be able to parse it easily with SimpleXML or with any other XML library."

I ended up pushing the xml namespace declarations right down into the appropriate elements - where I now think they are *supposed* to be:

<content:encoded>
<!--[CDATA[<doap:version
doap="http://usefulinc.com/ns/doap#"
sf="http://lcrouch-703.sb.sf.net/api/sfelements.rdf#">
<doap:name>Project 1.1 - Foobaj</doap:name>
<doap:created>1202221896</doap:created>
<doap:helper>
<foaf:person
foaf="http://xmlns.com/foaf/0.1/"
rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<foaf:name>admin1</foaf:name>
<foaf:homepage resource="http://lcrouch-703.sb.sf.net/users/admin1">
<foaf:mbox_sha1sum>6dd817a0f71590a68131a5e83b1bd73944654e8d</foaf:mbox_sha1sum>
</foaf:Person>
</doap:helper>
<doap:file-release>proj1.file1.tgz</doap:file-release>
<sf:download-count>0</sf:download-count>
</doap:Version>]]-->
</content:encoded>

Voila - SimpleXML starts parsing everything very easily.

This is one of the biggest boons for Test-Driven Development - the effects it has on the way you design your code. If I had not tested my code as an actual client would use it, I would have produced some pretty shoddy feeds with useless XML namespacing.