No such thing as a small change | |
PerlMonks |
Testing legacy CGI scripts using the "main() unless caller()" techniqueby davebaker (Pilgrim) |
on Jul 20, 2022 at 19:58 UTC ( [id://11145607]=perlquestion: print w/replies, xml ) | Need Help?? |
davebaker has asked for the wisdom of the Perl Monks concerning the following question: After reading the excellent Perl Testing: A Developer's Notebook by Ian Langworth and chromatic, I decided to implement a strategy described therein (pp. 163-166), to do some first-time testing on the many subroutines that are in a slew of cgi scripts (some of which are many hundreds of lines long). Most of them have many subroutines. One approach would be to put some or all of the subroutines into new modules, which makes for much easier testing of those subroutines. But the other strategy is to wrap the operational guts of the cgi script in a new "sub main() { # guts here }" that stops just before the subroutines are defined (which are always at the end of my cgi scripts). Then a testing script using Test::More can do a require_ok('path-to-cgi-script.cgi'); ... followed on the next line by a call to one of its subroutines, e.g.,ok( add_two_numbers(2,3) == 5, "2 plus 3 yields 5"); ... even though "add_two_numbers" isn't being exported by a module and is just a subroutine defined somewhere in the cgi script. Because the guts are embedded in a new "main()" subroutine, they don't actually get run (e.g., it doesn't generate the usual web page to STDOUT) when the testing script is run despite a require_ok() in the testing script, due to my adding a magic line to the top of the cgi script, as prescribed in the Perl Testing book:
caller() returns the caller's package name when invoked in scalar context. There always will be a caller when the testing script does a "require_ok('path-to-cgi-script.cgi');" because, of course, the cgi script is being called by the testing script. Unless the testing script has done something fancy in the way of giving itself a package name, caller() returns the string 'main' in scalar context. In boolean context, it returns "true" (if the script is being called). Now here's the interesting thing -- the book says it's necessary to have the script being tested (a cgi script, in my example) end with the statement "1;" so that the require_ok() in the testing script will always work (unless, of course, require_ok can't find the file or encounters a compilation error in the script). (Actually, the example in the book uses require() rather than require_ok(), but it's the same idea, that the script being called needs a "1;" at the end.) The first cgi script I've modified for testing does NOT in fact have a "1;" at the end, and does not have any other reason to be returning "true" as the value of its last expression. It's just the guts (now embedded in main()) and a bunch of subroutines. But require_ok() passes! So what's the deal? The hunt began! To make a long story short, it seems to me that, whenever require_ok() is run in a testing script, there is by necessity a caller from the perspective of the cgi script being tested, and what happens when the "main() unless caller();" line is encountered is that the "caller()" part/expression of the "main() unless caller();" statement is evaluated to "true", which turns out to be the value of the last expression evaluated by require_ok() -- the remaining lines of the tested script are just more subroutines that aren't being invoked. So now I think I know why require_ok() always gets a "true" value even without any final "1;", if a "main() unless caller();" statement is used at the "top" of the script being tested. Did I find a "mistake" in the wonderful Perl Testing book? I know that adding a "1;" to the end of the script is no big deal, but it rattled me that the book was so definite in this regard while my results differed. And, to make it more interesting (but consistent with my conclusion), what happens if the first line is written with this syntax instead?
Answer: the require_ok() in the test script FAILS this time, unless there's a "1;" at the end of the script being tested, despite the logic in the foregoing alternative first line being identical to the logic of the "main() unless caller();" statement. The reason, I think: the boolean test of "(! caller())" will return "false" any time there is a caller (i.e., whenever the script is being tested). So require_ok() gets no "true" love from that line. Then it continues down the tested script slurping up the lines and looking for an expression that returns true, but when it finds no such expression after passing over the last of the subroutines (none of which are invoked), it causes the require_ok() test to fail -- there was no "true" value returned from the file, which is required (ahem) for success by require_ok(). (Edited in an effort to improve clarity and brevity.)
Back to
Seekers of Perl Wisdom
|
|