.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Test::BDD::Cucumber::Manual::Steps 3pm" .TH Test::BDD::Cucumber::Manual::Steps 3pm "2023-08-29" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Test::BDD::Cucumber::Manual::Steps \- How to write Step Definitions .SH "VERSION" .IX Header "VERSION" version 0.86 .SH "INTRODUCTION" .IX Header "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. .SH "STARTING OFF" .IX Header "STARTING OFF" Most of your step files will want to start something like: .PP .Vb 1 \& #!perl \& \& package my_step_functions_for_feature_X; \& \& use strict; \& use warnings; \& \& use Test::More; # \*(Aquse Test2::V0;\*(Aq is also supported \& use Test::BDD::Cucumber::StepFile; .Ve .PP The fake shebang line gives some hints to syntax highlighters, and \&\f(CW\*(C`use strict;\*(C'\fR and \f(CW\*(C`use warnings;\*(C'\fR are hopefully fairly standard at this point. .PP Most of \fImy\fR Step Definition files make use of Test::More, but you can use any Test2 or Test::Builder based testing module. E.g. \f(CW\*(C`Test2::V0\*(C'\fR or \&\f(CW\*(C`Test::Exception\*(C'\fR. .PP Test::BDD::Cucumber::StepFile gives us the functions \f(CW\*(C`Given()\*(C'\fR, \f(CW\*(C`When()\*(C'\fR, \&\f(CW\*(C`Then()\*(C'\fR and \f(CW\*(C`Step()\*(C'\fR. .SH "STEP DEFINITIONS" .IX Header "STEP DEFINITIONS" .Vb 3 \& Given qr/I have (\ed+)/, sub { \& S\->{\*(Aqcount\*(Aq} += $1; \& }; \& \& When "The count is an integer", sub { \& S\->{\*(Aqcount\*(Aq} = \& int( S\->{\*(Aqcount\*(Aq} ); \& }; \& \& Then qr/The count should be (\ed+)/, sub { \& is( S\->{\*(Aqcount\*(Aq}, C\->matches\->[0], "Count matches" ); \& }; .Ve .PP 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 Test::BDD::Cucumber::StepContext object. Before the subref is executed, localized definitions of \f(CW\*(C`S\*(C'\fR and \f(CW\*(C`C\*(C'\fR are set, such that the lines below are equivalent: .PP .Vb 3 \& # 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\->{\*(Aqscenario\*(Aq}; $stash\->{\*(Aqcount\*(Aq} = 1 } \& sub { S\->{\*(Aqcount\*(Aq} = 1 } .Ve .PP We will evaluate the regex immediately before we execute the coderef, so you can use \f(CW$1\fR, \f(CW$2\fR, \f(CW$etc\fR. Similarly you can access named matches using \f(CW$+{match_name}\fR. .SS "Accessing step, scenario and feature properties" .IX Subsection "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.: .PP .Vb 4 \& # 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 .Ve .PP For a full review of available properties, see Test::BDD::Cucumber::Model::Step, Test::BDD::Cucumber::Model::Scenario and Test::BDD::Cucumber::Model::Feature respectively. .SS "Re-using step definitions" .IX Subsection "Re-using step definitions" Sometimes you want to call one step from another step. You can do this via the \&\fIStepContext\fR, using the \f(CW\*(C`dispatch()\*(C'\fR method. For example: .PP .Vb 4 \& Given qr/I have entered (\ed+)/, sub { \& C\->dispatch( \*(AqGiven\*(Aq, "I have pressed $1"); \& C\->dispatch( \*(AqGiven\*(Aq, "I have pressed enter", { some => \*(Aqdata\*(Aq } ); \& }; .Ve .PP For more on this topic, check the Redispatching section in the documentation for \f(CW\*(C`Test::BDD::Cucumber::StepContext\*(C'\fR. .SH "LOCALIZATION" .IX Header "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. .PP .Vb 5 \& # 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 .Ve .PP To see which keywords (and sub names) to use, ask pherkin about a specific language: .PP .Vb 7 \& > pherkin \-\-i18n de \& | feature | "Funktionalität" | \& | background | "Grundlage" | \& ... \& | given (code) | "Angenommen", "Gegebensei", "Gegebenseien" | \& | when (code) | "Wenn" | \& | then (code) | "Dann" | .Ve .PP 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 \f(CW\*(C`Wenn ich 1 gedrückt habe\*(C'\fR, could be: .PP .Vb 1 \& #!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\->{\*(AqCalculator\*(Aq}\->press($_) for split( /(,| und) /, C\->matches\->[0] ); \& }; .Ve .PP For more extensive examples see \fIexamples/i18n_de/\fR and \fIexamples/i18n_es\fR. .SH "ADDITIONAL STEPS" .IX Header "ADDITIONAL STEPS" Next to the steps that will be matched directly against feature file input, a number of additional step functions are supported: .IP "\(bu" 4 \&\f(CW\*(C`Before\*(C'\fR and \f(CW\*(C`After\*(C'\fR .Sp These steps create hooks into the evaluation process of feature files. E.g. .Sp .Vb 3 \& Before sub { # Run before every scenario \& # ... scenario set up code \& }; \& \& After sub { # Run after every scenario \& # ... scenario tear down code \& }; .Ve .Sp For more extensive hook functionality, see Test::BDD::Cucumber::Extension. .IP "\(bu" 4 \&\f(CW\*(C`Transform\*(C'\fR .Sp The \f(CW\*(C`Transform\*(C'\fR 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 \&\f(CW\*(C`Given\*(C'\fR, \f(CW\*(C`When\*(C'\fR and \f(CW\*(C`Then\*(C'\fR steps: a regular expression and a code reference. E.g. .Sp .Vb 2 \& Transform qr/^(\ed+)$/, 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; \& }; .Ve .SH "BEST PRACTICES" .IX Header "BEST PRACTICES" When writing step files, it's a good idea to take a few things into account. .IP "\(bu" 4 Declare a \f(CW\*(C`package\*(C'\fR at the top of your step file .Sp 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 \f(CW\*(C`Test::BDD::Cucumber::StepFile\*(C'\fR, which may lead to errors being reported in that package, even though they occur in your step file (which is confusing). .Sp The default package may change in the future and it will likely not be seeded with the content of the \f(CW\*(C`T::B::C::StepFile\*(C'\fR package. .IP "\(bu" 4 Declare a different \f(CW\*(C`package\*(C'\fR per step file .Sp 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. .Sp An example where this will be the case is when some of your step files are written using \f(CW\*(C`Test::More\*(C'\fR and some others are written using \f(CW\*(C`Test2::Bundle::More\*(C'\fR \*(-- both export a function \f(CW\*(C`ok\*(C'\fR, but with conflicting function prototypes. .IP "\(bu" 4 Don't define functions in your step file .Sp Especially step files provided by extensions. Step files may be loaded more than once, depending on the exact scenario in which \f(CW\*(C`App::pherkin\*(C'\fR 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. .SH "NEXT STEPS" .IX Header "NEXT STEPS" How step files are loaded is discussed in Test::BDD::Cucumber::Manual::Architecture, but isn't of much interest. Of far more interest should be seeing what you have available in Test::BDD::Cucumber::StepContext... .SH "AUTHOR" .IX Header "AUTHOR" Peter Sergeant \f(CW\*(C`pete@clueball.com\*(C'\fR .SH "LICENSE" .IX Header "LICENSE" .Vb 2 \& Copyright 2019\-2023, Erik Huelsmann \& Copyright 2011\-2019, Peter Sergeant; Licensed under the same terms as Perl .Ve