Ah, yes, one of my favorite subjects :)

The single greatest benefit that I have gained from tests is that I am a better programmer. It's not just that I can run my tests and know that my code works; by writing my tests first, I am forced to look at things from the standpoint of interfaces and who will be using my code. I need things to be simple and intuitive. As a result, my code is more likely to be simple and intuitive. Quite frequently, after a heavy refactoring of legacy code, I find myself with a module that has a bunch of one line methods that rarely take more than one argument (other than $self). That makes me feel good.

And I always find strange bugs that I never thought about, but I also find myself writing fewer bugs since I am focusing on the simplest thing that can get the tests to pass.

The non-deterministic factor is what worries me. That's actually quite a debate at my current job. Currently, we create test data, put it in a database and then nod when we get the test data back out. I think that's a problem because we're testing our expections or what the data should look like rather than testing what the data actually is. The rebuttal is "we can't test it if we don't know what it returns". In other words, they're afraid of non-deterministic tests.

There are plenty of ways to get around this. We can make raw SQL queries against a copy of the live data to verify that the code returns the correct data. We can use regular expressions to verify that the form of the data that we return is correct, or subroutines that return true or false depending upon whether or not the data is in the correct domain (e.g, day of week is 1 to 7 instead of an integer).

Finally, you can use Test::MockObject to override the difficult interfaces. If you find this is too much overhead, you can also override interfaces directly in your code. Here's one handy method. The subroutine you test calls fetchrow_arrayref internally:

my $results; my $test_data = [qw{foo bar baz}]; { no strict 'refs'; local $^W; # suppres redefined warnings my $arrayref_called = 0; local *{'DBI::fetchrow_arrayref'} = sub {$arraref_called++; $test_ +data }; $results = some_func($some_arg); is($arrayref_called, 1, '... and fetchrow_arrayref should be calle +d only once'); } ok($results, '... and some_func succeeded'); is_deeply($results, $test_data, '... and it should return the correct +data');

Finally, for troublesome built-ins that can be overridden, try ex::override.

my @rand = qw/ 0.78195951257749 0.625570958105044 0.884315045123127 0.137177303290578 0.0650888668725038 /; use ex::override rand => sub {unshift @rand => pop @rand; $rand[0]}; print rand, $/ for 1 .. 5;

Now you have deterministic "random" numbers :)

Cheers,
Ovid

New address of my CGI Course.


In reply to Re: TDD: a test drive and a confession by Ovid
in thread TDD: a test drive and a confession by dragonchild

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.