Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Web Developer

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

    Search

    XML Feeds

    Google Ads

    « Encoding to UTF-8 in phpFighting the automatic spam »
    comments

    Writing tests for code is just as important as writing the code itself. Time spent writing tests is less time spent tracking down and fixing bugs, making it a great investment. Despite knowing this it can sometimes be hard to force yourself to stop coding and write tests. Fortunately, Perl has some modules to make it pretty simple. Here's the module we're going to be testing:

    1. #!/usr/bin/perl
    2.  
    3. package MyMaths;
    4. use strict;
    5.  
    6. sub new
    7. {
    8.         my $proto = shift;
    9.         my $class = ref($proto) || $proto;
    10.         my $self = {};
    11.  
    12.         bless($self, $class);
    13.         return $self;
    14. }
    15.  
    16. sub add
    17. {
    18.         my $self = shift;
    19.         my $total = 0;
    20.         foreach my $factor (@_)
    21.         {
    22.                 $total += $factor;
    23.         }
    24.         return $total;
    25. }
    26.  
    27. 1;

    As you can see, we have a simple class with one method, add, which adds together all the arguments passed to it. We now need to define a test file for this module, called mymaths.t:

    1. #!/usr/bin/perl
    2.  
    3. use Test::More tests=>6;
    4. use MyMaths;
    5.  
    6. $mymaths = new MyMaths;
    7. is( $mymaths->add(1,2,3), 6, "1 + 2 + 3 = 6");
    8. is( $mymaths->add(6,2), 8,"6 + 2 = 8");
    9. is( $mymaths->add(1,2,3,4), 10, "1 + 2 + 3 + 4 = 10");
    10. is( $mymaths->add(1,2), 3, "1 + 2 = 3");
    11. is( $mymaths->add(2), 2, "2 = 2");
    12. is( $mymaths->add(2,-1), 1, "2 + -1 = 1");

    This test file uses the is() method of the builtin Test::More module. This method takes 3 arguments: the test, the expected result and a description. If a test fails, is() will give some feedback on the test.

    edit: To run the tests use the `prove` utility which is part of the Test::Harness package:

    prove -v mymaths.t

    Run all tests in the current directory with:
    prove -v .

    To run the tests we use another builtin module, Test::Harness. Create tests.pl:

    1. #!/usr/bin/perl
    2.  
    3. use Test::Harness qw(&runtests);
    4.  
    5. @tests = @ARGV ? @ARGV : <*.t>;
    6.  
    7. runtests @tests;

    This script can either be run with a list of test files as arguments, or with no arguments to run all test files. Running the script results in:

    1. ./mymaths.t ..
    2. 1..6
    3. ok 1 - 1 + 2 + 3 = 6
    4. ok 2 - 6 + 2 = 8
    5. ok 3 - 1 + 2 + 3 + 4 = 10
    6. ok 4 - 1 + 2 = 3
    7. ok 5 - 2 = 2
    8. ok 6 - 2 + -1 = 1
    9. ok
    10. All tests successful.
    11. Files=1, Tests=6,  0 wallclock secs ( 0.01 usr  0.02 sys +  0.02 cusr  0.01 csys =  0.06 CPU)
    12. Result: PASS

    Everything went fine, as expected. Now go back to the original class definition and make a typo on line 22, so it reads = instead of += and rerun the tests:

    1. ./mymaths.t ..
    2. 1..6
    3. not ok 1 - 1 + 2 + 3 = 6
    4. not ok 2 - 6 + 2 = 8
    5. not ok 3 - 1 + 2 + 3 + 4 = 10
    6. not ok 4 - 1 + 2 = 3
    7. ok 5 - 2 = 2
    8. not ok 6 - 2 + -1 = 1
    9.  
    10. #   Failed test '1 + 2 + 3 = 6'
    11. #   at ./mymaths.t line 7.
    12. #          got: '3'
    13. #     expected: '6'
    14.  
    15. #   Failed test '6 + 2 = 8'
    16. #   at ./mymaths.t line 8.
    17. #          got: '2'
    18. #     expected: '8'
    19.  
    20. #   Failed test '1 + 2 + 3 + 4 = 10'
    21. #   at ./mymaths.t line 9.
    22. #          got: '4'
    23. #     expected: '10'
    24.  
    25. #   Failed test '1 + 2 = 3'
    26. #   at ./mymaths.t line 10.
    27. #          got: '2'
    28. #     expected: '3'
    29.  
    30. #   Failed test '2 + -1 = 1'
    31. #   at ./mymaths.t line 12.
    32. #          got: '-1'
    33. #     expected: '1'
    34. # Looks like you failed 5 tests of 6.
    35. Dubious, test returned 5 (wstat 1280, 0x500)
    36. Failed 5/6 subtests
    37.  
    38. Test Summary Report
    39. -------------------
    40. ./mymaths.t (Wstat: 1280 Tests: 6 Failed: 5)
    41.   Failed tests:  1-4, 6
    42.   Non-zero exit status: 5
    43. Files=1, Tests=6,  0 wallclock secs ( 0.03 usr  0.00 sys +  0.03 cusr  0.00 csys =  0.06 CPU)
    44. Result: FAIL

    Most of the tests failed and gave good feedback to help find the problem.

    Summary

    Although writing tests can be a chore it is worth doing. If you write tests a little at a time:

    • When you design a class.
    • When you add a method to a class.
    • When you find a bug.

    You'll develop a good library of tests without too much effort.

    Further reading: Test::More perldoc

    3238 views
    oop, perl, testing
    InstapaperVote on HN

    9 comments

    Comment from: Andrew Parker [Visitor]
    Andrew Parker Nice article about the basics of using the perl testing modules. You can get away, however, without writing your own test.pl to run the tests by using the prove app instead, which is included in modern perls. It provides a lot of nice features for running the tests such as simple verbose mode and outputting the results with color.
    01/15/11 @ 16:13
    Comment from: Rob Earl [Member] Email
    Rob Earl Very nice, thank you for bringing that to my attention!
    01/15/11 @ 16:19
    Comment from: Tom Legrady [Visitor] Email · http://which-dwarf-are-you.blogspot.com/
    Tom Legrady I don't want to be negative, it's wonderful that you're introducing Test::More to people who may not have come across it before.

    I know the actual code being tested is not relevant, but why do you create a class which doesn't do anything class-like. If you want a subroutine which adds together all the numbers it receives, you could have a subroutine that, well, adds together all the arguments it receives. If you want it to be a class, then do something class-like, like add the arguments to an internal 'sum' attribute. You could have an add() method, a print() method, and a reset() method ... or maybe add() returns the sum.

    Also, adding (6, 2) and (1, 2) cover more or less the same ground. There is validity to running add() with zero arguments, with 1, 2, 3 arguments, running it with a negative argument, 2 negatives, with a zero value, with floats, with non-numerics.
    01/15/11 @ 19:53
    Comment from: Tom Legrady [Visitor] Email · http://which-dwarf-are-you.blogspot.com/
    Tom Legrady By the way, the Perl tradition is to have a sub-directory 't' in a program source directory, containing tests xyz.t.

    I use a numeric prefix, to control the order they are run in, combined with a descriptive word: 00-Constructor.t, 10-Methods.t, 20-Options.t.

    Then I have a 'tests' target in the Makefile, so I can run 'make tests'.
    01/15/11 @ 20:01
    Comment from: Gabor Szabo [Visitor] Email · http://szabgab.com
    Gabor Szabo I am happy to see people outside the Ironman bloggers ( http://ironman.enlightenedperl.org/ ) write about Perl and about testing Perl.

    Unfortunately the color scheme is a bit hard on my eyes (I can hardly see the curly braces and the "my" keywords) but I love the fact that the keywords are linked to their respective documentation.
    01/16/11 @ 00:25
    Comment from: Rob Earl [Member] Email
    Rob Earl @Tom: Constructive negative feedback is the best kind! My main focus was on the mechanic for writing and running the tests themselves but you're right, I should put a little more effort into making sensible examples if I'm going to blog about it.
    01/16/11 @ 05:57
    Comment from: Kyle [Visitor] · http://www.technokyle.com
    Kyle wow this is pretty epic. I don't really know that there's a module for testing perl scripts. don't really know TTD as I hack all the way to get my work than. But this might be a good introductory read to get an overview of how things work
    01/16/11 @ 08:01
    Comment from: kk [Visitor]
    kk A couple of things I noticed
    1. Is there any reason to use ref($proto) || $proto, it is not required

    2. There is a Core module called List::Util that has a sum method , you can use that.
    01/16/11 @ 09:31
    Comment from: Naveed Massjouni [Visitor] Email
    Naveed Massjouni Great article, thanks. It shows how easy it is to write tests with perl. I wish instead of showing the tests.pl with the Test::Harness stuff, you showed that you can just run:

    prove -v mymaths.t

    Or even just:

    perl mymaths.t

    Also, I enjoy writing tests in this "DSL" style:

    is $mymaths->add(1,2) => 3, "1 + 2 = 3";
    01/16/11 @ 19:13

    Leave a comment


    Your email address will not be revealed on this site.

    To mislead the spambots.

    Your URL will be displayed.
    (Line breaks become <br />)
    (Name, email & website)
    (Allow users to contact you through a message form (your email will not be revealed.)