Axa Assurance Maroc - Insurer Innovation Award 2024
Testing scripts
1. Testing Scripts
Randal L. Schwartz, merlyn@stonehenge.com
Version LT-1.05 on 13 Jun 2012
This document is copyright 2012 by Randal L. Schwartz, Stonehenge Consulting Services, Inc.
This work is licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License
http://creativecommons.org/licenses/by-nc-sa/3.0/
Monday, June 25, 12 1
2. • Problem:
• Ya gotta test!
• Solution:
• use Test::More and friends
• But:
• What about scripts!
Monday, June 25, 12 2
3. • Problem:
• scripts are separate process
• hard to mock things
• Solution:
• Don’t use a separate process
• Require your script in your .t
• But:
• How will I invoke it then?
Monday, June 25, 12 3
4. • Problem:
• Loose code is effectively “main”
• Solution:
• Bundle loose code into a run subroutine:
sub run { ... }
• Also ensure true value at end of script
• But:
• What will invoke “run” then?
Monday, June 25, 12 4
5. • Problem:
• Invoke “run” when run normally
• Don’t invoke “run” via require
• Solution:
• Use “caller”:
run(@ARGV) unless caller;
• But:
• What about namespace of .t file
Monday, June 25, 12 5
6. • Problem:
• Collision between script and .t names
• Solution:
• Bring it into its own package:
BEGIN {
package Program;
require "your-script";
die $@ if $@;
}
• But:
• How to “invoke the program” from tests?
Monday, June 25, 12 6
7. • Problem:
• Simulate execution
• Solution:
• Invoke run() with desired @ARGV:
subtest try_it => sub {
Program::run(qw(--foo --bar abc));
};
• But:
• What about exceptions, exit, stdout?
Monday, June 25, 12 7
8. • Problem:
• Trapping everything (not just die)
• eval doesn’t cut it!
• Solution:
• Test::Trap!
use Test::Trap;
trap {
Program::run(qw(--foo --bar abc));
};
• But:
• How will I know how the code finished?
Monday, June 25, 12 8
9. • Problem:
• Was it normal exit, “exit”, or die?
• Solution:
• examine $trap object after trap { .. }
ok $trap->exit, 0, "exited 0";
like $trap->die, qr{missing args};
• But:
• What about stdout, stderr, warnings?
Monday, June 25, 12 9
10. • Problem:
• What about those outputs?
• Solution:
• Test::Trap captures those too!
like $trap->stdout, qr{usage};
is $trap->stderr, q{}, "quiet errors";
is @{$trap->warn}, 1, "exactly 1 warn";
• But:
• What about stubbing or mocking?
Monday, June 25, 12 10
11. • Problem:
• Want to override some behavior
• Solution:
• Monkey patching!
subtest stub_it => sub {
local *Program::some_sub = sub { ... };
trap { Program::run() };
};
• But:
• What about stdin?
Monday, June 25, 12 11
12. • Problem:
• Provide stdin for script
• Solution:
• Small matter of programming:
local *STDIN;
open STDIN, "<", (my $S = join(q{}));
$$S .= "onentwonthreen";
trap { ... };
$$S .= "fournfiven"; trap { ... };
• But:
• What about chdir?
Monday, June 25, 12 12
13. • Problem:
• chdir has global effect
• Solution:
• Test::Trap is pluggable!
use Test::Trap::mine qw(:cwd);
trap { chdir "/tmp"; Program::run() };
• See my blog, or might be core now
• But:
• Does this really work for all scripts
Monday, June 25, 12 13
14. • Problem:
• Script might need complex interaction
• Maybe can’t edit code into run()
• Code might fork
• Solution:
• Yeah, traditional subprocesses
• Perhaps combined with Expect
• But:
• Test::Trap is amazingly useful!
Monday, June 25, 12 14
15. Follow me
• Twitter: @merlyn
• G+: Randal L. Schwartz
• Personal blog: merlyn.posterous.com
• http://blogs.perl.org/users/randal_l_schwartz/
• merlyn, realmerlyn, or RandalSchwartz
Monday, June 25, 12 15