<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>sometimes nothin&apos; can be a real cool hand</title>
      <link>http://www.martyandrews.net/blog/</link>
      <description></description>
      <language>en</language>
      <copyright>Copyright 2008</copyright>
      <lastBuildDate>Mon, 03 Mar 2008 12:50:38 +1000</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/?v=3.2</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>ruby files as libraries or scripts</title>
         <description><![CDATA[<p>
Include the following snippet at the end of a Ruby file:
</p>

<blockquote class="code">
<pre>
if $0 == __FILE__
    # do something
end
</pre>
</blockquote>

<p>
If you execute the file, the 'do something' block will run, but if you require the file, it won't.  That way you can use the file as both a library and a script.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2008/03/ruby_files_as_libraries_or_scr.html</link>
         <guid>http://www.martyandrews.net/blog/2008/03/ruby_files_as_libraries_or_scr.html</guid>
         <category>dev</category>
         <pubDate>Mon, 03 Mar 2008 12:50:38 +1000</pubDate>
      </item>
            <item>
         <title>Classification does not equal prioritisation</title>
         <description><![CDATA[<p>
One of the classic mistakes that people make in planning agile software projects comes about when someone is asked to prioritise the stories to be done by the software team.  The conversation usually goes like this:
</p>

<p>
Q: What do you think about Story A?<br/>
A: That's a priority one.
</p>

<p>
Q: Ok, what do you think about Story B?<br/>
A: That's a priority one as well.
</p>

<p>
At this point, the stories have <strong>not</strong> been prioritised.  They have been classified into groups, where the group is named &quot;Priority One&quot;.  Whilst this may be a useful culling technique, do not fool yourself into thinking they are prioritised.  What's worse for you is that, whether you like it or not, the stories will get done in some order.  If the team is ready to start on a story and finds two of them that are both &quot;Priority One&quot;, then they will pick one of them to do first.  Sometimes, they will pick the wrong one.
</p>

<p>
Do your team a favour.  Don't classify your stories.  Prioritise them.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2008/02/classification_does_not_equal.html</link>
         <guid>http://www.martyandrews.net/blog/2008/02/classification_does_not_equal.html</guid>
         <category>dev</category>
         <pubDate>Sun, 10 Feb 2008 22:10:19 +1000</pubDate>
      </item>
            <item>
         <title>Logging in Ruby on Rails</title>
         <description><![CDATA[<p>
Ruby on Rails has a bunch of built in configuration items that are related to logging.  You can set them up in your environment.rb file, or any of the specific environment override files.  Here's an example of that file with all of the attributes listed:
</p>

<blockquote class="code">
<pre>
Rails::Initializer.run do |config|
  config.logger = Logger.new
  config.log_level = :debug
  config.log_path = "log/myfilename.log"
  config.whiny_nils = true
  config.active_record.logger = Logger.new
  config.active_record.colorize_logging = false
  config.action_controller.logger = Logger.new
  config.action_mailer.logger = Logger.new
end
</pre>
</blockquote>

<p>
The <code>logger</code> attributes allow you to create a global logger, or a specific logger for the active record, action controller and action mailer framweworks.  The <code>log_level</code> controls how much logging gets printed out (we'll see an example a bit later).  <code>log_path</code> defines the location of your log file.  <code>whiny_nils</code> gives you some more verbose output if you call a method on a nil object, and <code>colorize_logging</code> gives you colored output in your logs, which is useful if you're viewing them in a *nix terminal window.
</p>

<p>
As an example, I can create a Rails controller like this.
</p>

<blockquote class="code">
<pre>
class SampleController < ApplicationController
  def do_logging
    logger.debug("what?")
    logger.info("done")
    logger.warn("uh oh")
    logger.error("bang!")
    logger.fatal("KABOOM")
  end
end
</pre>
</blockquote>

<p>
When executing the <code>do_logging</code> method on this class in a newly created Rails application, the following output appears in the terminal window:
</p>

<blockquote class="code">
<pre>
what?
done
uh oh
bang!
KABOOM
</pre>
</blockquote>

<p>
I don't find this very useful, so I try configuring my Rails applications wth <a href="http://log4r.sourceforge.net/">log4r</a> instead.  Here's an example of my setup in the development.rb file.
</p>

<blockquote class="code">
<pre>
config.log_level = :debug
Log4r::Logger.root.level = Log4r::DEBUG
formatter = Log4r::PatternFormatter.new(:pattern => "[%5l] %d %30C - %m")
Log4r::StderrOutputter.new('console', :formatter => formatter)
Log4r::Logger.new('App').add('console')
RAILS_DEFAULT_LOGGER = Log4r::Logger.new('App::Rails')
</pre>
</blockquote>

<p>
This has substituted the Rails logger with a Log4r logger instead.  The pattern formatter has been configured to print out the log level, date, context (logger name) and a log message.  I've created a top level logger call 'App', and another logger called 'Rails' which is a child of the 'App' logger.  The call to Logger.new with the parameter 'App::Rails' hooked the two together by name.  Log4r loggers write to their parents output as well as their own, so I don't need to create any more outputters.  Finally, I set my newly created logger to be the default Rails logger.  Running my controller again now produces this output:
</p>

<blockquote class="code">
<pre>
[DEBUG] 2007-06-26 11:45:56              App::Rails - what?
[ INFO] 2007-06-26 11:45:56              App::Rails - done
[ WARN] 2007-06-26 11:45:56              App::Rails - uh oh
[ERROR] 2007-06-26 11:45:56              App::Rails - bang!
[FATAL] 2007-06-26 11:45:56              App::Rails - KABOOM
</pre>
</blockquote>

<p>
That's much more useful now, however, I don't have any meaningful way of distinguishing log messages coming from the Rails framework to log messages coming from my code.  Framework log messages will all have the context of 'App::Rails' too.  I'd like to have a different context.  I can now change my controller to be like this though.
</p>

<blockquote class="code">
<pre>
require "log4r"

class SampleController < ApplicationController
  def initialize
    @@log = Log4r::Logger.new('App::SampleController')
  end
  
  def do_logging
    @@log.debug("what?")
    @@log.info("done")
    @@log.warn("uh oh")
    @@log.error("bang!")
    @@log.fatal("KABOOM")
  end
end
</pre>
</blockquote>

<p>
Making this change gets me the following output:
</p>

<blockquote class="code">
<pre>
[DEBUG] 2007-06-26 11:45:56   App::SampleController - what?
[ INFO] 2007-06-26 11:45:56   App::SampleController - done
[ WARN] 2007-06-26 11:45:56   App::SampleController - uh oh
[ERROR] 2007-06-26 11:45:56   App::SampleController - bang!
[FATAL] 2007-06-26 11:45:56   App::SampleController - KABOOM
</pre>
</blockquote>

<p>
Now I can easily tell apart my log messages from the Rails framework messages.
</p>

<p>
You may have noticed that the Log4r configuration I used in development mode only outputs to the console, and not to a file as happens by default.  I find that much more useful, as I don't have to bother cleaning up development log files any more.  One final Log4r trick that I use is in production mode is to use a different outputter that automatically creates a new log file every day and names the files according to date.  My production.rb file looks like this:
</p>

<blockquote class="code">
<pre>
config.log_level = :warn
Log4r::Logger.root.level = Log4r::WARN
formatter = Log4r::PatternFormatter.new(:pattern => "[%5l] %d %30C - %m")
Log4r::DateFileOutputter.new('logfile', :dirname => "log", :formatter => formatter)
Log4r::Logger.new('App').add('logfile')
RAILS_DEFAULT_LOGGER = Log4r::Logger.new('App::Rails')
</pre>
</blockquote>

<p>
Those settings makes it easy to manage log files in production mode.  Some people have reported possible errors when multiple mongrel processes try to roll a log file at the same time, and I haven't tested it in anger.  Be careful that you test if you get into that situation.  Otherwise, this setup has worked quite well for me.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/09/logging_in_ruby_on_rails.html</link>
         <guid>http://www.martyandrews.net/blog/2007/09/logging_in_ruby_on_rails.html</guid>
         <category></category>
         <pubDate>Sun, 02 Sep 2007 12:46:42 +1000</pubDate>
      </item>
            <item>
         <title>Ruby articles in Builder AU</title>
         <description><![CDATA[<p>
<a href="http://www.cogentconsulting.com.au/blogs/steveh/">Steve</a> has been invited to contribute regularly to the <a href="http://www.builderau.com.au/program/ruby/">Ruby series of articles in Builder AU</a>.  His first article, <a href="http://www.builderau.com.au/program/ruby/soa/Kicking-off-with-Ruby/0,339028320,339280966,00.htm">&quot;Kicking off with Ruby&quot</a>, has just been published.  Stay tuned for more.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/08/ruby_articles_in_builder_au.html</link>
         <guid>http://www.martyandrews.net/blog/2007/08/ruby_articles_in_builder_au.html</guid>
         <category></category>
         <pubDate>Wed, 15 Aug 2007 23:58:04 +1000</pubDate>
      </item>
            <item>
         <title>Ruby Nuby Screencasts</title>
         <description><![CDATA[<p>
The <a href="http://groups.google.com/group/melbourne-ruby/">Melbourne Ruby Group</a> recently ran a Ruby Nuby night where they got a bunch of different people to do five minute presentations on various aspects of Ruby and Rails.  I went along and recorded all of the presentations as screencasts.  You can see them all on the <a href="http://www.cogentconsulting.com.au/screencasts/">Cogent Consulting screencasts</a> page.
</p>

<p>
If Ruby on Rails is something you think you want to get into, we're running an introductory <a href="http://www.cogentconsulting.com.au/services/rubyOnRails.html">Ruby on Rails workshop</a> on July 21.  You can find out the details of our innovative pricing model and bid for a spot via our <a href="http://www.cogentconsulting.com.au/services/eat.html">Easy Access Training</a> page.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/07/ruby_nuby_screencasts.html</link>
         <guid>http://www.martyandrews.net/blog/2007/07/ruby_nuby_screencasts.html</guid>
         <category></category>
         <pubDate>Sat, 07 Jul 2007 17:17:43 +1000</pubDate>
      </item>
            <item>
         <title>Total financial transparency</title>
         <description><![CDATA[<p>
One of the principles of the <a href="http://www.cogentconsulting.com.au/manifesto.html">Cogent Consulting manifesto</a> is that all financial information, including all salary information, is totally open.  Of course, that's not so hard to deal with when there's only one or two of you at similar levels.  Now, we have eight people working with Cogent, and the model is still holding strong.  Come pay day, we send out a single email that has everyone's details in it.
</p>
<p>
Using this model from day one has actually been quite liberating.  Financial conversations that are commonly quite taboo in Australia are totally open amongst Cogent employees.  When anyone joins Cogent now, this model is one of the key points that I make to them before they start.  Any potential staff have to be completely comfortable with all other staff knowing their income details.  If they're not, Cogent is probably not the place for them.
</p>
<p>
Having said all of this, current Cogent staff members are all employed on a contract basis.  Their rates are determined by the clients they work for, not by Cogent itself.  Steve and I have decided recently to hire a permanent staff member, and of course, the same rules must apply to them.  We're trying to be creative about structuring a package that is appealing to the high quality people we want to attract, but also gets us some reward for the financial risk we're taking.
</p>
<p>
It remains to be seen whether this model will work or not, but its an experiment we absolutely want to partake in.  Building a company some other way isn't acceptable to us.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/06/total_financial_transparency.html</link>
         <guid>http://www.martyandrews.net/blog/2007/06/total_financial_transparency.html</guid>
         <category>work</category>
         <pubDate>Sun, 03 Jun 2007 21:24:38 +1000</pubDate>
      </item>
            <item>
         <title>I don&apos;t think you&apos;re a dickhead.  You&apos;re hired.</title>
         <description><![CDATA[<p>
Last week I had lunch with someone who wanted to work under a contract arrangement through <a href="http://www.cogentconsulting.com.au/">Cogent Consulting</a>.  You could notionally call the meeting an interview, but you'd be using the term loosely.  You see, <a href="http://www.cogentconsulting.com.au/blogs/steveh/">Steve</a> had spent over a year working with them at a previous organisation.  There was no technical information I could figure out from that meeting that was going to be more useful than the experience Steve had had working with them.  That left me with one main task - to figure out whether the culture fit was ok.
</p>
<p>
About 45 minutes into lunch after we'd all been chatting about various topics, I turned to Steve and said "well - my dickhead-ometer hasn't gone off, so he's ok by me".  And that was how our latest Cogent staff member joined us.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/05/i_dont_think_youre_a_dickhead_1.html</link>
         <guid>http://www.martyandrews.net/blog/2007/05/i_dont_think_youre_a_dickhead_1.html</guid>
         <category></category>
         <pubDate>Sun, 06 May 2007 19:12:54 +1000</pubDate>
      </item>
            <item>
         <title>Aggregated blogs for Cogent Consulting</title>
         <description><![CDATA[<p>
I've just finished putting together an aggregated blogs site for Cogent Consulting.  It includes blog entries from all Cogent staff and associates.  Its also the first Ruby app that I've written which has gone 'live'.  The code was much simpler than I expected it to be.  It's less than 50 lines of code, and about half of that is the declarations of all of the bloggers on the site.
</p>
<p>
Check out <a href="http://blogs.cogentconsulting.com.au/">http://blogs.cogentconsulting.com.au/</a>.
</p>
]]></description>
         <link>http://www.martyandrews.net/blog/2007/04/aggregated_blogs_for_cogent_co.html</link>
         <guid>http://www.martyandrews.net/blog/2007/04/aggregated_blogs_for_cogent_co.html</guid>
         <category></category>
         <pubDate>Fri, 20 Apr 2007 09:29:09 +1000</pubDate>
      </item>
            <item>
         <title>Changing static factories to a testable design.</title>
         <description><![CDATA[<p>
One of the <a href="http://jamesladdcode.com/">attendees</a> of the recent <a href="http://wiki.cogentconsulting.com.au/confluence/display/PUB/Design+Improvement%2C+March+24%2C+2007">design improvement workshop</a> that I ran sent me a question today.  I've taken the liberty of changing the code a bit to demonstrate the example, but the intent is basically the same.
</p>

<blockquote>
<p>
When we did the design improvement workshop we discussed how static methods can be a code smell. I'm wondering if you think this holds true for static factories since I don't feel right about creating a new instance of a factory just to ask it for an object ?
</p>

<p>
For example, I would normally have a factory like this:
</p>

<blockquote class="code">
<pre>
public class ThingFactory {
    public static Thing create() {
        // ...
    }
}
</pre>
</blockquote>

<p>
So the calling code would look like this:
</p>

<blockquote class="code">
<pre>
public class ThingProcessor {
    public void process() {
        Thing myThing = ThingFactory.create();
        // ...
    }
}
</pre>
</blockquote>

<p>
I'm a little anxious about code that looks like this:
</p>

<blockquote class="code">
<pre>
public class ThingProcessor {
    public void process() {
        Thing myThing = new ThingFactory().create();
        // ...
    }
}
</pre>
</blockquote>

<p>
Since we create two object's and only one is kept.
</p>
</blockquote>

<p>
The problem with static methods is that they make unit testing harder.  You can't override them in a subclass, and you can't mock them out.  So the implementation you've got is the only one that can be called.  Here's how I would implement the factory from the example above:
</p>

<blockquote class="code">
<pre>
public interface ThingFactory {
    public Thing create();
}

public class DefaultThingFactory implements ThingFactory {
    public Thing create() {
        // ...
    }
}
</pre>
</blockquote>

<p>
Now that I have an interface, I've got a mechanism that allows me to mock a method call.  A useful trick to use in conjunction with that is to allow the actual implementation to be used by default in production code.  You can do that with a combination of constructors like this:
</p>

<blockquote class="code">
<pre>
public class ThingProcessor {
    private final ThingFactory _factory;

    // This constructor lets you inject a mock for testing.
    public ThingProcessor(ThingFactory factory) {
        _factory = factory;
    }

    // This constructor can be used by production code that just wants the default factory.
    public ThingProcessor() {
        this(new DefaultThingFactory());
    }

    public void process() {
        Thing myThing = _factory.create();
        // ...
    }
}
</pre>
</blockquote>

<p>
The second design is much easier to test, yet the API for users of the ThingProcessor is exactly the same.
</p>
]]></description>
         <link>http://www.martyandrews.net/blog/2007/04/changing_static_factories_to_a.html</link>
         <guid>http://www.martyandrews.net/blog/2007/04/changing_static_factories_to_a.html</guid>
         <category></category>
         <pubDate>Fri, 13 Apr 2007 22:42:27 +1000</pubDate>
      </item>
            <item>
         <title>Screencast - software complexity on agile projects</title>
         <description><![CDATA[<p>
About a week ago, I did a presentation to the <a href="http://tech.groups.yahoo.com/group/melbourne_XP_enthusiasts/">Melbourne XP Enthusiasts Group</a> about software complexity.  I recorded the presentation as a screencast.  The audio is a recording of me presenting to the live audience, so it hasn't been created specifically for viewing on video.  It is still quite watchable though.
</p>
<p>
If you're interested in seeing it, it is now <a href="http://www.cogentconsulting.com.au/screencasts/index.html">available for viewing</a> on the Cogent Consulting web site.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/04/screencast_software_complexity.html</link>
         <guid>http://www.martyandrews.net/blog/2007/04/screencast_software_complexity.html</guid>
         <category></category>
         <pubDate>Fri, 13 Apr 2007 11:14:32 +1000</pubDate>
      </item>
            <item>
         <title>Why aren&apos;t you changing your desk layout?</title>
         <description><![CDATA[<p>
It seems that almost every place I go and consult at, developers are stuck in cubes with corner desks.  You've got to be kidding me people!  You all seem to know that software is about <a href="http://www.redhillconsulting.com.au/blogs/simon/archives/000395.html">collaboration</a>.  You even laugh at the antics of <a href="http://www.dilbert.com/">Dilbert</a> and his cube farm life.  Yet you don't seem to be doing anything about it.
</p>
<p>
When you go home tonight, grab yourself a phillips head screwdriver.  Bring it in to work tomorrow and make some adjustments.  If you have some draws on your desk, take them off.  If you have a return, take that off too.  Put the draws somewhere nearby so you can still use them, but not under your desk.  Now, take your monitor out of the corner, and move it to the square part of your desk.
</p>
<p>
Those simple modifications should mean that you now have enough room at your desk for two people to sit side by side and work at the same machine.  Try asking a colleague to come over and give you some feedback on a bit of work you've just done.  If she makes a suggestion, slide the keyboard over and ask her to show you a quick example of what she means.  Amazing isn't it!  Who would've thought that for no cost at all, you could get such a quick and noticeable improvement in the way you collaborate with people at work.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/04/why_arent_you_changing_your_de_1.html</link>
         <guid>http://www.martyandrews.net/blog/2007/04/why_arent_you_changing_your_de_1.html</guid>
         <category></category>
         <pubDate>Wed, 04 Apr 2007 16:58:06 +1000</pubDate>
      </item>
            <item>
         <title>Screencasts of Keynote presentations with Snapz Pro X</title>
         <description><![CDATA[<p>
The <a href="http://www.ambrosiasw.com/support/faqs/faq.php?forum=75&title=Snapz%20Pro%20X&version=2.0.3&link=utilities/snapzprox/#157">Snapz Pro X FAQ</a> suggests that you can't record Keynote presentations because they take over the whole screen, basically because it uses the hardware directly rather than going through the software layers.  Trying to record a screencast of a keynote video results in a constant shot of the last screenshot before the presentation started.
</p>
<p>
It turns out there is a way to get this to work though.  The Keynote preferences has an option under the Slideshow settings called "Allow Expose, Dashboard and others to use the screen".  Ticking that box will solve the problem and let you record as per normal.  It claims that performance may be affected as a result, but I didn't notice any difference on my MacBook Pro.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/03/screencasts_of_keynote_present.html</link>
         <guid>http://www.martyandrews.net/blog/2007/03/screencasts_of_keynote_present.html</guid>
         <category></category>
         <pubDate>Wed, 28 Mar 2007 11:09:54 +1000</pubDate>
      </item>
            <item>
         <title>Technorati Blog Claim</title>
         <description><![CDATA[<p>This entry only exists in order to "claim my blog" on Technorati.</p>

<p><a href="http://technorati.com/claim/v9chgdsn39" rel="me">Technorati Profile</a></p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/03/technorati_blog_claim.html</link>
         <guid>http://www.martyandrews.net/blog/2007/03/technorati_blog_claim.html</guid>
         <category></category>
         <pubDate>Fri, 02 Mar 2007 16:54:16 +1000</pubDate>
      </item>
            <item>
         <title>March Design Improvement EAT Workshop</title>
         <description><![CDATA[<p>
Places are filling up fast for the <a href="http://wiki.cogentconsulting.com.au/confluence/display/PUB/Design+Improvement%2C+March+24%2C+2007">March Desgn Improvement workshop</a> that I'm running under the Cogent Consulting <a href="http://www.cogentconsulting.com.au/services/eat.html">Easy Access Training</a> program on March 24th.  If you're interested in attending, get your bids in quick.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/02/march_design_improvement_eat_w.html</link>
         <guid>http://www.martyandrews.net/blog/2007/02/march_design_improvement_eat_w.html</guid>
         <category></category>
         <pubDate>Tue, 27 Feb 2007 08:57:50 +1000</pubDate>
      </item>
            <item>
         <title>How many tests do you need?</title>
         <description><![CDATA[<p>
The number of tests you need for your system depends on how complex it is.  Obvious, right?  The tricky part is knowing how to measure that.  There are a couple of good mathematical definitions that can help.
</p>

<p>
The <i>Cyclomatic Complexity</i> of a method is the number of linearly independent paths through a program's source code.  This means that the Cyclomatic Complexity of a method is the minimum number of unit tests needed to test every path in a method.
</p>

<p>
Take this piece of code as an example:
</p>

<blockquote class="code">
<pre>

public static String getFormattedName(String givenName, String middleName, String surname) {
    if (givenName != null) {
        givenName = givenName.trim();
    } else {
        givenName = "";
    }

    if (middleName != null) {
        middleName = middleName.trim();
    } else {
        middleName = "";
    }

    if (surname != null) {
        surname = surname.trim();
    } else {
        surname = "";
    }

    return givenName + " " + middleName + " " + surname;
}

</pre>
</blockquote>

<p>
Every method starts out with a default Cyclomatic Complexity of one.  The complexity is then increased by one for every linearly independent path through that method.  In this case, each <i>if</i> statement in the method represents an extra path.  So the total Cyclomatic Complexity for the method is four.
</p>

<p>
In other words, if you were writing tests for this method, you should write at least four of them.
</p>

<p>
The <i>NPath Complexity</i> of a method is the number of acyclic execution paths through that method.  This means that the NPath Complexity basically represents all of the combinations of paths possible through a method, and is an upper bound on the number of tests you need to write for a method.
</p>

<p>
Lets use the same example code again.
</p>

<p>
Every method starts out with a default NPath Complexity of one.  The complexity is then increased by one for every nested conditional loop, and multiplied by two for every parallel conditional loop.  In this case, the method starts out with a complexity of one.  It is multiplied by two at the first <i>if</i> block, bringing it up to two.  It is multiplied by two again at the second <i>if</i> block, bringing it up to four.  It is multiplied by two yet again at the last <i>if</i> block, bringing it up to eight.
</p>

<p>
In other words, if you were writing tests for this method, you should write up to eight of them.
</p>

<p>
So the number of tests you need to write is:
</p>

<h3 style="text-align:center;"><strong>Cyclomatic Complexity <= Number of Tests <= NPath Complexity</strong></h3>

<p>
The hard part now is to try and calculate what your complexity is.  Fortunately, <a href="http://www.martyandrews.net/resources/complexian.html">Complexian</a> can do that for you.
</p>]]></description>
         <link>http://www.martyandrews.net/blog/2007/02/how_many_tests_do_you_need.html</link>
         <guid>http://www.martyandrews.net/blog/2007/02/how_many_tests_do_you_need.html</guid>
         <category></category>
         <pubDate>Sat, 24 Feb 2007 10:35:30 +1000</pubDate>
      </item>
      
   </channel>
</rss>
