NAME¶
Test::BDD::Cucumber::Manual::Tutorial - Quick Start Guide
VERSION¶
version 0.11
Introduction¶
In this article we're going to jump straight in to using Test::BDD::Cucumber to
build some simple tests for Digest, a core Perl module which provides message
digests.
We'll create a "features/" directory, and put our first test case in
it, "features/basic.feature" in it. The contents of it are, in their
entirity:
# Somehow I don't see this replacing the other tests this module has...
Feature: Simple tests of Digest.pm
As a developer planning to use Digest.pm
I want to test the basic functionality of Digest.pm
In order to have confidence in it
Background:
Given a usable Digest class
Scenario: Check MD5
Given a Digest MD5 object
When I've added "foo bar baz" to the object
And I've added "bat ban shan" to the object
Then the hex output is "bcb56b3dd4674d5d7459c95e4c8a41d5"
Then the base64 output is "1B2M2Y8AsgTpgAmY7PhCfg"
Scenario: Check SHA-1
Given a Digest SHA-1 object
When I've added "<data>" to the object
Then the hex output is "<output>"
Examples:
| data | output |
| foo | 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 |
| bar | 62cdb7020ff920e5aa642c3d4066950dd1f01f4d |
| baz | bbe960a25ea311d21d40669e93df2003ba9b90a2 |
Scenario: MD5 longer data
Given a Digest MD5 object
When I've added the following to the object
"""
Here is a chunk of text that works a bit like a HereDoc. We'll split
off indenting space from the lines in it up to the indentation of the
first \"\"\"
"""
Then the hex output is "75ad9f578e43b863590fae52d5d19ce6"
This is a complete test, and if you run pherkin against it, you will get sane
output! It just doesn't do anything ... yet.
In the "features/" we'll add a "step_definitions/"
directory, and add our first (and again, only) step definitions
"features/step_definitions/basic_steps.pl" file in it:
#!perl
use strict;
use warnings;
use Test::More;
use Test::BDD::Cucumber::StepFile;
use Method::Signatures;
Given qr/a usable (\S+) class/, func ($c) { use_ok( $1 ); };
Given qr/a Digest (\S+) object/, func ($c) {
my $object = Digest->new($1);
ok( $object, "Object created" );
$c->stash->{'scenario'}->{'object'} = $object;
};
When qr/I've added "(.+)" to the object/, func ($c) {
$c->stash->{'scenario'}->{'object'}->add( $1 );
};
When "I've added the following to the object", func ($c) {
$c->stash->{'scenario'}->{'object'}->add( $c->data );
};
Then qr/the (.+) output is "(.+)"/, func ($c) {
my $method = {base64 => 'b64digest', 'hex' => 'hexdigest' }->{ $1 } ||
do { fail("Unknown output type $1"); return };
is( $c->stash->{'scenario'}->{'object'}->$method, $2 );
};
When you run pherkin or the Test::Builder-based test which does the same thing
("900_run_features.
thttps://github.com/sheriff/test-bdd-cucumber-perl/blob/master/t/900_run_features.t"
in t), we look for a "features/" directory, and search for step
definitions files (matched by "*_steps.pl") and feature files
(matched by "*.feature").
The step matchers (the code that starts with "Given", "When"
and "Then") are all loaded, and then we execture the feature files
one by one. Let's step through the feature file, and look at how it matches up
to the step definitions file.
Name and conditions of satisfaction¶
Feature: Simple tests of Digest.pm
As a developer planning to use Digest.pm
I want to test the basic functionality of Digest.pm
In order to have confidence in it
The first non-comment line of your feature file is a description of what you
intend to do. You need to start the name itself with the string
"Feature:", and that should be the first line of your file, save
comments (denoted by #).
Anything after that before the next new-line are your conditions of
satisfaction. These aren't parsed, they're treated as human-readable text, and
by convention, they're a user story
<
http://en.wikipedia.org/wiki/User_story>.
Background¶
Background:
Given a usable Digest class
Next up, we have the Background section. The Background is a special kind of
Scenario that doesn't have an explicit name, and should occur only once in
your feature file. Its steps are run before the steps of every other scenario
- the harnesses distributed with this distro won't display the Background
section separately, they'll just subsume the steps in to the other scenarios.
This is matched by:
Given qr/a usable (\S+) class/, func ($c) { use_ok( $1 ); };
"Given()" is a function exported by Test::BDD::Cucumber::StepFile that
accepts two arguments: a regular expression (or a string when you don't need
to do any smart matching) and a coderef. We use Method::Signatures to save
some typing here, so note that written longer hand, the above might be
written:
Given(
qr/a usable (\S+) class/,
sub {
my $c = shift;
use_ok( $1 );
};
);
If you're paying attention, you might notice that "use_ok" comes from
Test::More.
Each step is run, from a Test::Builder
perspective,
as its own distinct test file. This happens seamlessly, so you can
use any Test::Builder-based testing tools in your step definitions without
really worrying about it. There's some more detail in
Test::BDD::Cucumber::Manual::Steps.
The First Scenario...¶
Scenario: Check MD5
Given a Digest MD5 object
When I've added "foo bar baz" to the object
And I've added "bat ban shan" to the object
Then the hex output is "bcb56b3dd4674d5d7459c95e4c8a41d5"
Then the base64 output is "1B2M2Y8AsgTpgAmY7PhCfg"
The first scenario is delimited from the previous steps by a blank line, and
it's called
Check MD5. Scenarios are marked out using the
"Scenario:" keyword, and just like the Background section before,
it's a series of steps. These steps rely on the step before, which means we
can examine the Test::Builder::StepContext object $c a little more closely.
Given qr/a Digest (\S+) object/, func ($c) {
my $object = Digest->new($1);
ok( $object, "Object created" );
$c->stash->{'scenario'}->{'object'} = $object;
};
Creates a step definition. We create a new Digest object, and then use
Test::More's "ok()" function to check that worked. We then put it in
the
stash for other steps to use. There are three stashes documented in
Test::Builder::StepContext, "feature", "scenario" and
"step". As you might expect, "feature" is available to all
step definitions that are being executed as part of a feature, and
"scenario" is available to all steps that are being executed as part
of a feature.
The context is the single argument that gets passed to each step, and it
contains evertything that step should need to execute. We'll be looking at
some of the methods you can call against it as we look at other steps, and you
can find complete documentation for it here: Test::Builder::StepContext.
This scenario also introduce several ways of starting a step,
Given,
When, and
Then, as well as
And. These are used to
organize steps by type, with
Given tending to describe setup steps,
When describing the key actions that you're testing, and
Then
describing the outputs you're looking for. You can find more on this here:
https://github.com/cucumber/cucumber/wiki/Given-When-Then
<
https://github.com/cucumber/cucumber/wiki/Given-When-Then>.
A step definition you've declared with
Given will not match a step
starting with
Then. You can use the keyword
Step to declare
general matching steps in your step definition files, although it's considered
bad practice.
Finally, the keywords
And and
But are simply replaced with the
verb on the line before them.
Scenario Outlines¶
Scenario: Check SHA-1
Given a Digest SHA-1 object
When I've added "<data>" to the object
Then the hex output is "<output>"
Examples:
| data | output |
| foo | 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 |
| bar | 62cdb7020ff920e5aa642c3d4066950dd1f01f4d |
| baz | bbe960a25ea311d21d40669e93df2003ba9b90a2 |
The next scenario adds one of the three ways you can provide structured data to
your steps, using placeholders and a table. This scenario is run three times,
one for each table row, and with the " <placeholders" > being
replaced by the appropriate row's column. These are called Scenario Outlines
<
https://github.com/cucumber/cucumber/wiki/Scenario-outlines>.
Multiline Step Arguments¶
Scenario: MD5 longer data
Given a Digest MD5 object
When I've added the following to the object
"""
Here is a chunk of text that works a bit like a HereDoc. We'll split
off indenting space from the lines in it up to the indentation of the
first \"\"\"
"""
Then the hex output is "75ad9f578e43b863590fae52d5d19ce6"
While before we were looking at structured data on a Scenario level, we can also
provide it on a Step level, in two ways. Firstly, we can provide multi-line
strings, as above, using a feature that is syntactically similar to
"pystring"s, and conceptually similar to HEREDOCs. The contents of
the string will be available to the step definition via the "data()"
method of the
context:
When "I've added the following to the object", func ($c) {
$c->stash->{'scenario'}->{'object'}->add( $c->data );
};
While we don't have an example of it here, you can also provide tables to your
steps, which will also be available via "data()":
Scenario: Sort Employees
Given a set of employees
| name | wage | hair color |
| Peter | 10,000 | brown |
| John | 20,000 | blond |
| Joan | 30,000 | green |
You can find out more about these features in the Cucumber documentation here:
https://github.com/cucumber/cucumber/wiki/Multiline-Step-Arguments
<
https://github.com/cucumber/cucumber/wiki/Multiline-Step-Arguments>.
Next Steps...¶
That's the tutorial done! You can find out more about writing steps in
Test::BDD::Cucumber::Manual::Steps, the documentation for our simple
command-line tool in App::pherkin, and how to integrate with Test::Builder in
Test::BDD::Cucumber::Manual::Integration.
AUTHOR¶
Peter Sergeant "pete@clueball.com"
LICENSE¶
Copyright 2011, Peter Sergeant; Licensed under the same terms as Perl