So I've got some code. It's not fancy code, but it should be under test and isn't. I'm usually a pretty strong TDD guy but when writing code that has lots of external interactions, in this case simple filesystem manipulations, it gets sufficiently sticky that I bail out and "just write the damn thing."

The algorithm is:

Here's the approximate look of it. (Note that in the live version the source and destination filenames are sufficiently less patterned that abstracting them further would just be annoying and not really grant me anything.)

# Version Zero sub post_process { my ($path,$yymmdd) = @_; my %rename_table = ("foo_data_a.results" => "bar_abc_foo_data_$yymmdd", ("foo_data_b.results" => "bar_bcd_foo_data_$yymmdd", ("foo_data_c.results" => "bar_cde_foo_data_$yymmdd", ("foo_data_d.results" => "bar_def_foo_data_$yymmdd", ("foo_data_e.results" => "bar_efg_foo_data_$yymmdd", # Don't do anything if any files are missing. for my $src_file (keys %rename_table) { if (!-f "$path/$src_file") { die "$src_file missing. Go blame someone.\n"; } } for my $src_file (keys %rename_table) { # Rename the file my $to_base = "$path/$rename_table{$src_file}"; rename "$path/$src_file","$to_base.results"; `touch $to_base.FLG`; } }
So, ok. No tests. So my "while 1: run tests, wait 5 seconds" test script yacks all over the place. Well, the first fix (for runing in a while(1) test loop) is easy...
# Version One sub post_process { my ($path,$yymmdd) = @_; my %rename_table = ("foo_data_a.results" => "bar_abc_foo_data_$yymmdd", ("foo_data_b.results" => "bar_bcd_foo_data_$yymmdd", ("foo_data_c.results" => "bar_cde_foo_data_$yymmdd", ("foo_data_d.results" => "bar_def_foo_data_$yymmdd", ("foo_data_e.results" => "bar_efg_foo_data_$yymmdd", # Don't do anything if any files are missing. for my $src_file (keys %rename_table) { if (!file_exists("$path/$src_file")) { die "$src_file missing. Go yell at Jorge.\n"; } } for my $src_file (keys %rename_table) { # Rename the file my $to_base = "$path/$rename_table{$src_file}"; rename_file("$path/$src_file","$to_base.results"); touch_file("$to_base.FLG"); } } sub file_exists { my ($filename) = @_; #return (-f $filename); return 1; } sub rename_file { my ($src,$dst) = @_; # return rename $src,$dst return 1; } sub touch_file { my ($filename) = @_; # return `touch $filename`; return 1; }

So now at least the file system operations are abstracted for testing. Now what I didn't do is add a global $TEST_MODE to the script and then pepper the abstraction functions with things like:

my $TEST_MODE = 1; sub file_exists { my ($filename) = @_; if ($TEST_MODE) { return 1; } return (-f $filename); }

...because while that DOES solve the "oh crap, I forgot to replace the mocks with the real versions in the live code" thing that happens, it also makes your code a godawful mess.

So it looks like I'm heading towards some weird "OSOps" class that I can mock out with a duplicated interface in "MockOSOps".

Now... This is getting complicated fast. I'm going from a twenty-something line function into a couple new modules. I'm pretty much sold on the idea that it's the right way to go, despite the fact that I'll end up with what really amounts to a "catch all module" full of stuff I can mock out.

But in the peculiarly touchy-feely language of the Agile Programmers,

It smells off somehow.

Or perhaps I just need another shower.

Thoughts?

Me

In reply to Unit testing OS rich code by Voronich

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.