File: Steps.pod

package info (click to toggle)
libtest-bdd-cucumber-perl 0.75-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 680 kB
  • sloc: perl: 7,905; makefile: 8; sh: 5
file content (256 lines) | stat: -rw-r--r-- 8,080 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package Test::BDD::Cucumber::Manual::Steps;

=encoding utf8

=head1 NAME

Test::BDD::Cucumber::Manual::Steps - How to write Step Definitions

=head1 VERSION

version 0.75

=head1 INTRODUCTION

The 'code' part of a Cucumber test-suite are the Step Definition files which
match steps, and execute code based on them. This document aims to give you a
quick overview of those.

=head1 STARTING OFF

Most of your step files will want to start something like:

 #!perl

 package my_step_functions_for_feature_X;

 use strict;
 use warnings;

 use Test::More; # 'use Test2::V0;' is also supported
 use Test::BDD::Cucumber::StepFile;

The fake shebang line gives some hints to syntax highlighters, and
C<use strict;> and C<use warnings;> are hopefully fairly standard at this point.

Most of I<my> Step Definition files make use of L<Test::More>, but you can use
any L<Test2> or L<Test::Builder> based testing module. E.g. C<Test2::V0> or
C<Test::Exception>.

L<Test::BDD::Cucumber::StepFile> gives us the functions C<Given()>, C<When()>,
C<Then()> and C<Step()>.

=head1 STEP DEFINITIONS

 Given qr/I have (\d+)/, sub {
    S->{'count'} += $1;
 };

 When "The count is an integer", sub {
    S->{'count'} =
        int( S->{'count'} );
 };

 Then qr/The count should be (\d+)/, sub {
    is( S->{'count'}, C->matches->[0], "Count matches" );
 };

Each of the exported verb functions accept a regular expression (or a string
that's used as one), and a coderef. The coderef is passed a single argument,
the L<Test::BDD::Cucumber::StepContext> object. Before the subref is executed,
localized definitions of C<S> and C<C> are set, such that the lines below are
equivalent:

  # Access the first match
  sub { my $context = shift; print $context->matches->[0] }
  sub { C->matches->[0] }

  # Set a value in the scenario-level stash
  sub { my $context = shift; my $stash = $context->stash->{'scenario'}; $stash->{'count'} = 1 }
  sub { S->{'count'} = 1 }

We will evaluate the regex immediately before we execute the coderef, so you
can use C<$1>, C<$2>, C<$etc>. Similarly you can access named matches using C<$+{match_name}>.

=head2 Accessing step, scenario and feature properties

Step functions have access to the various properties of the step, scenario and feature in
which they're being used. This includes tags, line numbers, etc. E.g.:

  # Examples of step properties
  C->step->line->number
  C->step->verb
  C->step->original_verb

  # Examples of scenario properties
  C->scenario->name
  C->scenario->tags

  # Examples of feature properties
  C->feature->name
  C->feature->tags
  C->feature->language

For a full review of available properties, see L<Test::BDD::Cucumber::Model::Step>,
L<Test::BDD::Cucumber::Model::Scenario> and L<Test::BDD::Cucumber::Model::Feature>
respectively.

=head2 Re-using step definitions

Sometimes you want to call one step from another step. You can do this via the
I<StepContext>, using the C<dispatch()> method. For example:

  Given qr/I have entered (\d+)/, sub {
        C->dispatch( 'Given', "I have pressed $1");
        C->dispatch( 'Given', "I have pressed enter", { some => 'data' } );
  };

For more on this topic, check the L<Redispatching|Test::BDD::Cucumber::StepContext/Redispatching>
section in the documentation for C<Test::BDD::Cucumber::StepContext>.

=head1 LOCALIZATION

Both feature files and step files can be written using non-english Gherkin keywords. A german
feature file could look like the example below.

  # language: de
  Funktionalität: Grundlegende Taschenrechnerfunktionen
    Um sicherzustellen, dass ich die Calculator-Klasse korrekt programmiert habe,
    möchte ich als Entwickler einige grundlegende Funktionen prüfen,
    damit ich beruhigt meine Calculator-Klasse verwenden kann.

    Szenario: Anzeige des ersten Tastendrucks
      Gegeben sei ein neues Objekt der Klasse Calculator
      Wenn ich 1 gedrückt habe
      Dann ist auf der Anzeige 1 zu sehen

To see which keywords (and sub names) to use, ask pherkin about a specific language:

 > pherkin --i18n de
 | feature          | "Funktionalität"                             |
 | background       | "Grundlage"                                  |
 ...
 | given (code)     | "Angenommen", "Gegebensei", "Gegebenseien"   |
 | when (code)      | "Wenn"                                       |
 | then (code)      | "Dann"                                       |

The last three lines of this list show you which sub names to use in your step
file as indicated by the '(code)' suffix. A corresponding step file specifying
a step function for C<Wenn ich 1 gedrückt habe>, could be:

  #!perl

  use strict;
  use warnings;
  use utf8;    # Interpret accented German chars in regexes and identifiers properly

  use Test::More;
  use Test::BDD::Cucumber::StepFile;


  Wenn qr/^ich (.+) gedrückt habe/, sub {
      S->{'Calculator'}->press($_) for split( /(,| und) /, C->matches->[0] );
  };

For more extensive examples see F<examples/i18n_de/> and F<examples/i18n_es>.

=head1 ADDITIONAL STEPS

Next to the steps that will be matched directly against feature file input, a
number of additional step functions are supported:

=over 4

=item * C<Before> and C<After>

These steps create hooks into the evaluation process of feature files. E.g.

   Before sub {  # Run before every scenario
      # ... scenario set up code
   };

   After sub {  # Run after every scenario
     # ... scenario tear down code
   };

For more extensive hook functionality, see L<Test::BDD::Cucumber::Extension>.

=item * C<Transform>

The C<Transform> step serves to map matched values or table rows from feature file
(string) input to step input values. The step takes two arguments, same as the
C<Given>, C<When> and C<Then> steps: a regular expression and a code reference. E.g.


   Transform qr/^(\d+)$/, sub {
      # transform matches of digit-only strings

      my $rv = $1;
      # ... do something with $rv
      return $rv;
   };

   Transform qr/^table:col1,col2$/, sub {
      # transform tables with 2 columns, named col1 and col2 respectively

      my ($step_context, $data) = @_;
      # ... transform data in $data
      return $data;
   };


=back


=head1 BEST PRACTICES

When writing step files, it's a good idea to take a few things into account.

=over 4

=item * Declare a C<package> at the top of your step file

By declaring a specific package (your own), you make sure not to step on internals of other modules.
At the time of writing, the default package is C<Test::BDD::Cucumber::StepFile>, which may lead to
errors being reported in that package, even though they occur in your step file (which is confusing).

The default package may change in the future and it will likely not be seeded with the content of
the C<T::B::C::StepFile> package.

=item * Declare a different C<package> per step file

By using different packages per step file (or group of step files), name spaces are isolated which
reduces the risk of importing functions with the same name from different packages.

An example where this will be the case is when some of your step files are written using C<Test::More>
and some others are written using C<Test2::Bundle::More> -- both export a function C<ok>, but with
conflicting function prototypes.

=item * Don't define functions in your step file

Especially step files provided by extensions. Step files may be loaded more than once, depending on
the exact scenario in which C<App::pherkin> is run. When the step files are being loaded multiple times,
there won't be any impact on step definition, but any function definitions will cause 'function redefined'
warnings.

=back

=head1 NEXT STEPS

How step files are loaded is discussed in
L<Test::BDD::Cucumber::Manual::Architecture>, but isn't of much interest. Of
far more interest should be seeing what you have available in
L<Test::BDD::Cucumber::StepContext>...

=head1 AUTHOR

Peter Sergeant C<pete@clueball.com>

=head1 LICENSE

  Copyright 2019-2020, Erik Huelsmann
  Copyright 2011-2019, Peter Sergeant; Licensed under the same terms as Perl

=cut

1;