File Coverage

blib/lib/Test/Cukes.pm
Criterion Covered Total %
statement 62 68 91.1
branch 13 16 81.2
condition 1 3 33.3
subroutine 11 12 91.6
pod 0 3 0.0
total 87 102 85.2


line stmt bran cond sub pod time code
1             package Test::Cukes;
2 5     5   146226 use strict;
  5         38  
  5         145  
3 5     5   46 use warnings;
  5         9  
  5         126  
4 5     5   2040 use Test::Cukes::Feature;
  5         21  
  5         231  
5 5     5   3528 use Carp::Assert;
  5         7170  
  5         35  
6 5     5   732 use Try::Tiny;
  5         13  
  5         289  
7              
8 5     5   34 use base 'Test::Builder::Module';
  5         13  
  5         3648  
9              
10             our $VERSION = "0.11";
11             our @EXPORT = qw(feature runtests Given When Then assert affirm should shouldnt);
12              
13             our @missing_steps = ();
14              
15             my $steps = {};
16             my $feature = {};
17              
18             sub feature {
19 3     3 0 174 my $caller = caller;
20 3         6 my $text = shift;
21              
22 3         106 $feature->{$caller} = Test::Cukes::Feature->new($text)
23             }
24              
25             sub runtests {
26 4     4 0 23 my $caller = caller;
27 4         10 my $feature_text = shift;
28              
29 4 100       14 if ($feature_text) {
30 1         37 $feature->{$caller} = Test::Cukes::Feature->new($feature_text);
31             }
32              
33 4         10 my @scenarios_of_caller = @{$feature->{$caller}->scenarios};
  4         166  
34              
35 4         13 for my $scenario (@scenarios_of_caller) {
36 4         81 my $skip = 0;
37 4         11 my $skip_reason = "";
38 4         35 my $gwt;
39              
40              
41 4         7 for my $step_text (@{$scenario->steps}) {
  4         152  
42 12         65 my ($pre, $step) = split " ", $step_text, 2;
43 12 50       44 if ($skip) {
44 0         0 Test::Cukes->builder->skip($step_text);
45 0         0 next;
46             }
47              
48 12 50       103 $gwt = $pre if $pre =~ /(Given|When|Then)/;
49              
50 12         25 my $found_step = 0;
51 12         39 for my $step_pattern (keys %$steps) {
52 21         38 my $cb = $steps->{$step_pattern}->{code};
53              
54 21 100       363 if (my (@matches) = $step =~ m/$step_pattern/) {
55 9         17 my $ok = 1;
56             try {
57 9     9   471 $cb->(@matches);
58             } catch {
59 0     0   0 $ok = 0;
60 9         76 };
61              
62 9         1736 Test::Cukes->builder->ok($ok, $step_text);
63              
64 9 50 33     3429 if ($skip == 0 && !$ok) {
65 0         0 Test::Cukes->builder->diag($@);
66 0         0 $skip = 1;
67 0         0 $skip_reason = "Failed: $step_text";
68             }
69              
70 9         16 $found_step = 1;
71 9         20 last;
72             }
73             }
74              
75 12 100       52 unless($found_step) {
76 3         5 $step_text =~ s/^And /$gwt /;
77 3         8 push @missing_steps, $step_text;
78             }
79             }
80             }
81              
82             # If the user doesn't specify tests explicitly when they use Test::Cukes;,
83             # assume they had no plan and call done_testing for them.
84 4 100       34 Test::Cukes->builder->done_testing if !Test::Cukes->builder->has_plan;
85              
86 4         2860 report_missing_steps();
87              
88 4         663 return 0;
89             }
90              
91             sub report_missing_steps {
92 4 100   4 0 17 return if @missing_steps == 0;
93 1         3 Test::Cukes->builder->note("There are missing step definitions, fill them in:");
94 1         396 for my $step_text (@missing_steps) {
95 3         600 my ($word, $text) = ($step_text =~ /^(Given|When|Then) (.+)$/);
96 3         24 my $msg = "\n$word qr/${text}/ => sub {\n ...\n};\n";
97 3         13 Test::Cukes->builder->note($msg);
98             }
99             }
100              
101             sub _add_step {
102 10     10   271 my ($step, $cb) = @_;
103 10         30 my ($package, $filename, $line) = caller;
104              
105 10         57 $steps->{$step} = {
106             definition => {
107             package => $package,
108             filename => $filename,
109             line => $line,
110             },
111             code => $cb
112             };
113             }
114              
115             *Given = *_add_step;
116             *When = *_add_step;
117             *Then = *_add_step;
118              
119             1;
120             __END__
121              
122             =head1 NAME
123              
124             Test::Cukes - A BBD test tool inspired by Cucumber
125              
126             =head1 SYNOPSIS
127              
128             Write your test program like this:
129              
130             # test.pl
131             use Test::Cukes;
132             # use Test::Cukes tests => 3;
133              
134             feature(<<TEXT);
135             Feature: writing behavior tests
136             In order to make me happy
137             As a test maniac
138             I want to write behavior tests
139              
140             Scenario: Hello World
141             Given the test program is running
142             When it reaches this step
143             Then it should pass
144             TEXT
145              
146             Given qr/the (.+) program is (.+)/, sub {
147             my ($program_name, $running_or_failing) = @_;
148             assert "running program '$program_name'";
149             };
150              
151             When qr/it reaches this step/, sub {
152             assert "reaches";
153             };
154              
155             Then qr/it should pass/, sub {
156             assert "passes";
157             };
158              
159             runtests;
160              
161             When it runs, it looks like this:
162              
163             > perl test.pl
164             1..3
165             ok 1 - Given the test program is running
166             ok 2 - When it reaches this step
167             ok 3 - Then it should pass
168              
169             =head1 DESCRIPTION
170              
171             Test::Cukes is a testing tool inspired by Cucumber
172             (L<http://cukes.info>). It lets your write your module test with
173             scenarios. It may be used with L<Test::More> or other family of
174             TAP C<Test::*> modules. It uses L<Test::Builder::note> function
175             internally to print messages.
176              
177             This module implements the Given-When-Then clause only in English. To
178             uses it in the test programs, feed the feature text into C<feature>
179             function, defines your step handlers, and then run all the tests by
180             calling C<runtests>. Step handlers may be defined in separate modules,
181             as long as those modules are included before C<runtests> is called.
182             Each step may use either C<assert> or standard TAP functions such as
183             C<Test::Simple>'s C<ok> or C<Test::More>'s C<is> to verify desired
184             result. If you specify a plan explicitly, you should be aware that
185             each step line in your scenario runs an additional test, and will
186             therefore add to the number of tests you must indicate.
187              
188             If any assertion in the Given block failed, the following C<When> and
189             C<Then> blocks are all skipped.
190              
191             You don't need to specify the number of tests with C<plan>. Each step
192             block itself is simply one test. If the block died, it's then
193             considered failed. Otherwise it's considered as passing.
194              
195             In the call to L<Test::Cukes::runtests>, L<done_testing> will automatically
196             be called for you if you didn't specify a plan.
197              
198             Test::Cukes re-exports C<assert> function from C<Carp::Assert> for you
199             to use in the step block.
200              
201             For more info about how to define feature and scenarios, please read
202             the documents from L<http://cukes.info>.
203              
204             =head1 AUTHOR
205              
206             Kang-min Liu E<lt>gugod@gugod.orgE<gt>
207              
208             =head1 CONTRIBUTORS
209              
210             Tatsuhiko Miyagawa, Tristan Pratt
211              
212             =head1 SEE ALSO
213              
214             The official Cucumber web-page, L<http://cukes.info/>.
215              
216             cucumber.pl, L<http://search.cpan.org/dist/cucumber/>, another Perl
217             implementation of Cucumber tool.
218              
219             L<Carp::Assert>
220              
221             =head1 LICENSE
222              
223             This is free software, licensed under:
224              
225             The MIT (X11) License
226              
227             =head1 DISCLAIMER OF WARRANTY
228              
229             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
230             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
231             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
232             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
233             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
234             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
235             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
236             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
237             NECESSARY SERVICING, REPAIR, OR CORRECTION.
238              
239             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
240             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
241             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE
242             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
243             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
244             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
245             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
246             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
247             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
248             SUCH DAMAGES.
249              
250             =cut