Testing scripts

1,255 views

Published on

My YAPC::NA 2012 Lightning Talk (5 minutes) on testing scripts using Test::Trap and other tools.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,255
On SlideShare
0
From Embeds
0
Number of Embeds
21
Actions
Shares
0
Downloads
7
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Testing scripts

  1. 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. 2. • Problem: • Ya gotta test! • Solution: • use Test::More and friends • But: • What about scripts!Monday, June 25, 12 2
  3. 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. 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. 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 fileMonday, June 25, 12 5
  6. 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. 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. 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. 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. 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. 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. 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. 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 scriptsMonday, June 25, 12 13
  14. 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. 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 RandalSchwartzMonday, June 25, 12 15

×