Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic

Testing Non-module code

by pboin (Deacon)
on Mar 16, 2004 at 11:55 UTC ( #336972=perlquestion: print w/replies, xml ) Need Help??
pboin has asked for the wisdom of the Perl Monks concerning the following question:

Like many of you folks, I've read PM for a while, and had that nagging 'I can do it better' thing hanging over my head for a while, and now I want to look into writing test cases.

I read some neat articles, and while I haven't tried it yet, I think I get the point about test cases being written and living in the 't' subdirectory.

Problem is, that those cases all seem to 'use' modules, get a new instance and work with it. A lot of my existing code isn't in Modules, it's plain ol' procedural code. Of course, I'm careful in error checking, etc, but how can I incorporate formal ok/'not ok' testing w/o having modules to call?

Oh, and a great big 'Thank You' to all PM folks, new and old!

Replies are listed 'Best First'.
Re: Testing Non-module code
by dragonchild (Archbishop) on Mar 16, 2004 at 13:18 UTC
    Well, your code is probably organized into subroutines with some defined set of inputs and outputs, right? So, test those subroutines! Create a set of sample inputs and expected outputs. (This is a good way to help decouple your subroutines, too.)

    Remember - testing an object still involves testing a bunch of subroutines. Yeah, there's some state and it can be easier to envision it, but it's still just a bunch of subroutines.

    If you're dealing with testing scripts and the like, I'd point you to Test::Cmd and Test::Cmd::Common.

    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Testing Non-module code
by matija (Priest) on Mar 16, 2004 at 14:14 UTC
    The reason the tests in examples use the modules is simple: that way, the tests can be in a separate file, and not in the main program file.

    The one thing you don't want to do is to be testing a copy of your routines: sooner or later (probably sooner) you would forget to update the testing copy from your current copy, and you would be wondering why the tests aren't affected by the changes you made. Using the same modules from the test program as well as from the program you're developing neatly avoids that.

    But there is an alternative. You could use Test::Simple (or any of the other test tools) from within the target application itself, probably only when it was called with a specil parameter like --run-tests.

    When such a parameter was given, you would simply run a testing procedure instead of the regular run procedure.

    I hope this makes things a bit clearer. Happy testing!

      I also have a lot of procedural code, some modules, and some subroutines. The (partially implemented) plan here is:
      • Use normal module testing for all the modules
      • Ditto subroutines
      • Use WWW::Mechanize in a test script to test the CGI
      • For all of the other programs, have the test script build a proper environment (set inputs and such) and then qx// the script to be tested. Then use the Test::More functions on the results. (If the program was supposed to create file '/tmp/foo', then do ok( -e '/tmp/foo' ).)

      An example of the last: the application I'm developing here has a series of Perl scripts that create and setup the initial database. The test script can test these by making sure there is no database of that name, then running the scripts in qx//, and checking to see if the database exists and has the right tables. (Quick aside: Always generating your db from a re-runnable, extendable script comes in handy later.)

      This will also work for scripts that are just supposed to read from one queue in the database and write to another: Set up something in the DB for the script to do, run the script, see if the output is good.

      Spring: Forces, Coiled Again!
        Use WWW::Mechanize in a test script to test the CGI

        If you're using Apache, you might like to check out Apache::Test. I've found it to provide a very usable framework.

      I see what you're saying -- it directly addresses one of my questions, and that is: how to separate testing behavior from 'real' operation.

      To some extent I'm already doing that, I have a sub called 'dbg' that prints the tell() and the passed message, but only when debug is turned on via getopt. I could easily do the same type of thing, but with testing.

      Thank you (both) very much. I work alone from home, and I love it, but the one thing I really miss (and it's a big negative) is peers. I can get pretty far down the 'wrong road' sometimes w/o someone to call me on a dumb idea.

Re: Testing Non-module code
by whiteEFunk (Acolyte) on Mar 16, 2004 at 18:46 UTC
    I found that writing tests along with the code helps me catch error conditions early. I use Test::More. Sometimes, the code I write is not intended to run in my development/test environment. In these cases I modify package objects, or data structures within the tests to get the desired scenario. Then I run a test against it.

    Test::More example: contains a function named foo_plus_one like this -
    sub foo_plus_one($) { return $_[0]+1 }
    You can 'require' your script into the test -
    BEGIN { require_ok("('')") };
    and call the functions -
    my $foo = 4; is( foo_plus_one($foo), '5', 'method foo_plus_one()' );

    pass output -
    ok 1 - method foo_plus_one()

    OR fail -
    not ok 1 - method foo_plus_one()
    # Failed test (foo.t at line 6)
    # got: '1003'
    # expected: '5'

    Testing is a great way to build a program! Ye-haw!

      Well, I didn't know where the question would lead exactly, but I think your post has the real key, and that is:

      To test a procedural script w/o affecting regular run-time behavior, 'require' the script into your test script so you have access to it's subroutines.

      Thanks to you and everyone, the PerlMonks never cease to impress me. With this kind of support, no wonder 4 out of 5 dentists who code prefer Perl!

Re: Testing Non-module code
by QM (Parson) on Mar 16, 2004 at 19:50 UTC
    I saw this on MJD's QOTW. See QOTW #14 Solution and the module. Maybe you can expound on this idea for your needs.

    In summary:

    Create a module (say YourTestModule), and in the INIT block calls a module sub that runs your tests, then exits (never actually running your program in full).

    Run your tests by:

    perl -MYourTestModule yourprogram
    I'd be interested in comments as to the usefulness of this approach for "serious" code. (Or perhaps there's a better way that hasn't been mentioned yet?)

    Quantum Mechanics: The dreams stuff is made of

Re: Testing Non-module code
by eserte (Deacon) on Mar 16, 2004 at 19:23 UTC
    With "ol' procedural code" do you mean collections of functions in .pl files or standalone scripts? If you mean the first, then you can do normal Test::* tests, just require the file instead use'ing it. In the second case you may consider to convert the script into a hybrid script/module --- this is not too hard, just move the running part below a return 1 if caller; line. Now you can require the file and test the subroutines in it without running the script. If you don't have subroutines at all, then you can still check for side effects (e.g. created or changed files, or screen output). If the script is interactive, then you could consider to use

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://336972]
Approved by Corion
Front-paged by broquaint
[choroba]: LanX do you mean s/add_style/ set_style/?
[LanX]: choroba: yes
[LanX]: line numbering is easy to fix with # line dircetives
[LanX]: I just wrote my first source filter, without changing the source
[LanX]: :)

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (11)
As of 2017-04-24 15:10 GMT
Find Nodes?
    Voting Booth?
    I'm a fool:

    Results (442 votes). Check out past polls.