Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

Unit testing built-in functions

by aijin (Monk)
on Nov 08, 2006 at 22:45 UTC ( #582988=perlquestion: print w/replies, xml ) Need Help??
aijin has asked for the wisdom of the Perl Monks concerning the following question:

We've started doing test-driven development at my office, and that means writing lots of unit tests.

My current task involves taking a bunch of pre-existing, scattered code and turning it into a module.

There are a lot of built-in calls in the code, and I'm not sure how to tackle them in the unit tests.

For example, for a call like this:

unlink( $filename );

I would like to be able to have that call fail on a file that should be there, but isn' if someone had deleted the file while the program was running. Or perhaps it exists, but can't be deleted.

I am familiar with Test::MockModule and Test::MockObject and was hoping that there was a module that mocked built-in calls so that I could manipulate them in the same way. So far, my search-fu has failed me and I can not find such a module.

Likely, I am missing an obvious solution. Would someone care to enlighten me?

Replies are listed 'Best First'.
Re: Unit testing built-in functions
by perrin (Chancellor) on Nov 08, 2006 at 23:30 UTC
    Overriding built-in functions is a FAQ.
Re: Unit testing built-in functions
by ikegami (Pope) on Nov 09, 2006 at 00:33 UTC

    You can only override some builtins, and those you can override must be overridden by importing the replacement functions from another module.


    use strict; use warnings; package TestHarness; BEGIN { our @EXPORT_OK = qw( unlink ); require Exporter; *import = \&Exporter::import; } sub unlink { print("PRE unlink\n"); # Don't forget to properly handle context. my $rv = unlink(@_); print("POST unlink\n"); return $rv; } 1;


    use strict; use warnings; BEGIN { # Parse options or something. $::TESTING = 1; } use if $::TESTING, TestHarness => qw( unlink ); unlink();


    PRE unlink POST unlink

    You could control TestHarness::unlink's behaviour using globals.

      You can only override some builtins, and those you can override must be overridden by importing the replacement functions from another module.

      The former: Yes.

      The latter: Um, no. Or at least, not quite:

      ~$ perl -le 'BEGIN { *CORE::GLOBAL::unlink = sub(@) { print "@_" } } u +nlink "aaa".."aad"' aaa aab aac aad

      print "Just another Perl ${\(trickster and hacker)},"
      The Sidhekin proves Sidhe did it!

Re: Unit testing built-in functions
by davidrw (Prior) on Nov 08, 2006 at 23:12 UTC
    per perldoc for unlink, it returns the number of files deleted .. so can you just change the calls to something like this?
    if( unlink( $filename ) != 1 ){ die "couldn't unlink '$filename': $!; } # or: unlink( $filename ) or die "couldn't unlink '$filename': $!;
    Your unit tests could also check for both the existance before the unlink, and non-existence afterward ...

      I believe you misunderstood. The OP wants to override unlink so he can test how the script handles errors from unlink. He's not asking how to check if unlink returned an error.

      We do something similar at my workplace. In our test environment, we hook into the OS to control the exit codes returned by the disk reading function, the analog input reading function, the analog output writing function, etc. to ensure that our software properly handles those errors.

        Yes, exactly.

        I want to get built-in functions to return specific values when called from specific test cases.

        It looks like over-rides and globals will be the easiest way to go so far.
Re: Unit testing built-in functions
by sgifford (Prior) on Nov 09, 2006 at 04:02 UTC
    Maybe this is a silly question, but why not just create/delete the appropriate file in the test setup, probably in a temporary directory, then check to make sure it's been deleted? Obviously it's a little more complicated than that, but it doesn't seem a lot more complicated...

      The reason you don't want to be manipulating the file system and/or database to set up your test cases is that you have to know a lot more about what's inside the code you're testing if you do that, you need to know that the module is going to go looking for a particular file, which file, what it will try to do to the file. (and if the system is written by monkeys who dont know<rant removed> like at $job, there will be hard coded paths, special cases, things that should be cleaned up but aren't...

      Also, if you can just automatically fail to remove the file (as per a mocked unlink) your tests will run a bundle faster, and you will be able to run the tests on production without having to worry (since it won't be axing the file your batch process just spent hours creating)

      And, with a mocked unlink, you can be sure that it's trying to remove the right file ie that the call unlink $file has the right thing in $file and you can trigger pass/fail based on that

      @_=qw/ ask foolish to appear and remain for a moment _ of pretend better than a lifetime /; s;;@{[map{$_[hex$_]}split'',C204316E89D2B4516EF]};;s/_ /\n/&print;

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://582988]
Approved by planetscape
Front-paged by andyford
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2018-05-24 16:50 GMT
Find Nodes?
    Voting Booth?