Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #22 from gojko/master

merged
  • Loading branch information...
commit 47a21ff1463b6ab19dc9d19f9b0e4f3fc24e76e2 2 parents 514d676 + 902042d
@davedf davedf authored
View
8 plainbook/about_the_doc.xml
@@ -2,16 +2,14 @@
<preface id="pre_about" title="About this document">
<p>This document is a step-by-step guide for Cucumber,
a tool that is quickly becoming the weapon of choice for many agile teams when it comes to functional test automation, creating
- executable specifications and building a living documentation. We believe in iterative development so we write and publish iteratively.</p>
+ executable specifications and building living documentation. We believe in iterative development so we write and publish iteratively.</p>
<section id="pre_about_whatsnew" title="What's new in this version">
<p>The changes in this version are:</p>
<ul>
- <li>Introduction cleaned up, added a section on how Cucumber fits into BDD, moved terminology to the Gherkin part</li>
- <li>Fixed PDF inclusion of some missing .NET sources</li>
- <li>and a lot of minor fixes and tweaks</li>
+ <li>a lot of minor fixes and tweaks</li>
</ul>
</section>
-<section id="pre_about_online" title="Online">
+<section id="pre_about_online" title="Get online">
<p>To get notified when we publish an update, grab the code for the examples we used in the guide,
provide feedback or ask a question, do one or more of the following: </p>
<ul>
View
2  plainbook/author.xml
@@ -7,7 +7,7 @@
hypothesis at the same time&rdquo;. Read David's blog at <url link="http://deflorinier.blogspot.com" />
or follow him on Twitter as @davedf.</p>
<p>
- Still active as a Bond-villain, Gojko Adzic gave up a
+ Still active as a Bond villain, Gojko Adzic gave up a
career in the Soviet spy service to pursue tougher challenges in
software development. All that remains of his past life now is the stupid accent.
Gojko's goal with this document is to send
View
182 plainbook/gherkin.xml
@@ -1,8 +1,8 @@
<!DOCTYPE chapter SYSTEM "../resources/plainbook/plainbook.dtd" >
<chapter title="Cucumber feature files" id="chp_feature_files">
- <p>In this chapter, we look into the basic structure of a Cucumber feature file. You will learn:</p>
+ <p>In this chapter, we look at the structure of a Cucumber feature file. You will learn:</p>
<ul>
- <li>The basic elements of feature files</li>
+ <li>The elements of feature files</li>
<li>How to write some basic scenarios so that Cucumber can understand them</li>
<li>What makes a good feature file</li>
</ul>
@@ -11,21 +11,21 @@
saying a wrong word, here are some of the basic phrases that you need to memorise:</p>
<section title="Stakeholder">
<p>
- A <define>Stakeholder</define> is a person who gets some value out of the system. Typical stakeholders are
+ A <define>stakeholder</define> is a person who gets some value out of the system. Typical stakeholders are
representative of groups of users, for example a trader or an administrator. Some teams describe
stakeholders with user personae
- (explained nicely in User Stories Applied<bib ref="cohn:userstory"/>). Personae are particularly useful
- for web based projects as they encourage you to think about how a typical user is likely to interact with
- the system&mdash; for example look at a monitor in despair.
+ (explained in <i>User Stories Applied</i><bib ref="cohn:userstory"/>). Personae are particularly useful
+ for web-based projects as they encourage you to think about how a typical user is likely to interact with
+ the system &ndash; for example, to look at a monitor in despair.
</p>
</section>
<section title="Feature">
<p>
A
- <define>Feature</define>
+ <define>feature</define>
is a piece of system functionality that delivers value to one or more stakeholders. Executable
- specifications work best when described from the perspective of stakeholders, not technical
- infrastructure, since doing so enables business users to understand them better and engage more in
+ specifications work best when described from the perspective of stakeholders, not software developers,
+ since this enables business users to understand them better and engage more in
discussions about what makes a particular feature complete.
</p>
<p>Features are normally vertical
@@ -39,7 +39,7 @@
<section title="User story">
<p>
A
- <define>User story</define>
+ <define>user story</define>
is a rough description of scope for future work, used as a planning tool. Stories are vertical
slices of system functionality that are deliverable independently. Each story should specify the
value it brings to one or more stakeholders, the stakeholders that care about the story
@@ -47,19 +47,21 @@
</p>
<quote>
- In order to..., as a ..., I want ...
+ In order to..., As a ..., I want ...
</quote>
<p>
There are alternative formats, such as:
</p>
<quote>As a ..., I want..., So that...</quote>
- <p>Many a ninja has died in the futile format wars and some silly shoguns will try to convince you that one way of telling stories is significantly better than the other one. Ninja cucumber disagrees! For the purposes of this document all these formats are equivalent and we won't enter into a discussion
+ <p>Many a ninja has died in the futile format wars and some silly shoguns will try to convince you that
+one way of telling stories is significantly better than the other one. Ninja cucumber disagrees! For the purposes of
+ this document all the formats are equivalent and we won't enter into a discussion
on which one is better.
</p>
<p>
- Stories often impact several features &mdash; a payment story might have an impact on card
+ Stories often impact several features &ndash; a payment story might have an impact on card
processing, fraud control and back-office reporting. Stories are often
- relatively small chunks of work to support frequent delivery. A single feature might be delivered through a large number of
+ relatively small chunks of work, helping developers to deliver new functionality frequently. A single feature might be delivered through a large number of
stories.
</p>
<note title="Stories are more than just the format">
@@ -69,7 +71,7 @@
<quote>In order to reduce fraud, as a financial controller I want the system to automatically
send Chuck Norris to beat up suspected fraudsters.
</quote>
- <p>This story clearly states who cares about some functinality, why it is important and what it delivers. This provides
+ <p>This story clearly states who cares about some functionality, why it is important and what it delivers. This provides
enough
information for a meaningful discussion on the specifications when the time comes to implement it.
Here is
@@ -89,27 +91,19 @@
</section>
<section title="Feature file">
<p>A
- <define>Feature file</define>
+ <define>feature file</define>
describes a feature or a part of a feature with representative examples of expected outcomes. Cucumber
uses these files to validate some system functionality against its specifications. Feature files are
plain-text files, ideally stored in the same version control system as the related project.
</p>
<p>Here is an example of a Cucumber feature file:</p>
<code file="ruby/NinjaSurvivalRate/features/ninja_survival_rate.feature"/>
- <p>Later in this chapter we cover how Cucumber interprets this file.
- </p>
- <note title="Feature file extension">
- <p>
- Feature files should have a
- <code>.feature</code>
- extension
- </p>
- </note>
+ <p>Later in this chapter we cover how Cucumber interprets this file.</p>
</section>
- <section title="Key Examples">
+ <section title="Key examples">
<p>
Each feature should be illustrated with
- <define>Key Examples</define>. The examples show the expected outcomes in specific representative
+ <define>key examples</define>. The examples show the expected outcomes in specific representative
cases with very precise
information. This makes it absolutely clear what the system is expected to do and helps to avoid
any misunderstanding and ambiguity. It also makes it possible for an automation tool, such as
@@ -119,10 +113,10 @@
<section title="Scenario">
<p>
A
- <define>Scenario</define>
+ <define>scenario</define>
captures one key example for a feature in the feature file. It represents a way that the system
delivers some value to a stakeholder. Scenarios are units of specifications for Cucumber. They
- allow us to chop up feature functionality like Sushi, into chunks that can be separately consumed.
+ allow us to chop up feature functionality like sushi, into chunks that can be separately consumed.
Examples of good scenarios for credit card processing might be successful
transaction authorisation, transaction failure due to lack of funds, transaction failure due to
wrong verification code and so on.
@@ -131,13 +125,14 @@
<section title="Step">
<p>
<define>Steps</define>
- are domain language phrases that we can combine to write scenarios. They can either refer to the context
- of a scenario, an action that the scenario describes or a way to validate the action. The steps that
+ are domain language phrases that we can combine to write scenarios. They can refer to the context
+ of a scenario, an action that the scenario describes, or a way to validate the action. The steps that
define the context of a scenario generally begin with the
<define>Given</define>
keyword, for example:
- <quote>Given the ninja has a third level black-belt</quote>
- The steps that define an action generally begin with the
+ <quote>Given the ninja has a third-level black belt</quote>
+
+The steps that define an action generally begin with the
<define>When</define>
keyword, for example:
<quote>When attacked by a samurai</quote>
@@ -147,9 +142,9 @@
<quote>Then the ninja should engage the opponent</quote>
</p>
</section>
- <section title="Step Definition">
+ <section title="Step definitions">
<p>
- <define>Step Definitions</define>
+ <define>Step definitions</define>
are reusable automation components that execute individual steps. They tell Cucumber what to do when
interpreting a particular step (see <link ref="img_cuke_steps"/>). Programmers or automation specialists
write step definitions where necessary when implementing scenarios to validate that they work correctly.
@@ -164,11 +159,11 @@
is the language for describing Cucumber feature files. It is also the name of a separate piece of
software that interprets that language.
</p>
- <p>Gherkin, the language, defines the structure of a feature
+ <p>Gherkin the language defines the structure of a feature
file and the keywords that can be used to describe features.
- Gherkin, the software, parses files for Cucumber and related projects, such as
+ Gherkin the software parses files for Cucumber and related projects, such as
Specflow.<footnote>
- <url link="http://specflow.org"/> a native .NET implementation
+ <url link="http://specflow.org"/> is a native .NET implementation
of a Gherkin-compliant automation tool
</footnote>
You can use it to implement your own version
@@ -202,29 +197,32 @@
<i>Then </i> the ninja should run for his life
</code>
<p>Each feature file starts with the
- <code>Feature</code> keyword (if in English, we'll deal with other languages later). The keyword is followed by a colon and the feature name. In this case, the feature name is:
+ <code>Feature</code> keyword (if in English, we'll deal with other languages later). The keyword is followed by a colon and the feature title. In this case, the feature title is:
<quote>Fight or flight</quote>
- The feature name helps a reader understand what the file is about in general.
+ The title of a feature should help a reader understand what the file is about.
<note>
A good technique for deciding on
a title is to think about what you would type into Google to search for this file if it were online.
</note>
</p>
<p>
- The paragraph which follows the feature title is a free-form text that describes the
- intention of the feature. In this case, the feature description is:
- <quote> In order to increase the ninja survival rate, As a ninja commander I want my ninjas to
+ The paragraph that follows the feature title is free-form text that describes the
+ purpose of the feature. In this case, the feature description is:
+ <quote> In order to increase the ninja survival rate, as a ninja commander I want my ninjas to
decide whether to take on an opponent based on their skill levels</quote>
- This paragraph should give the reader a context to understand the rest of
+This paragraph should provide the context to enable the reader to understand the rest of
the file.
- Describing features in the user story format is a common practice, although they do not
- necessarily map directly to user stories. To describe a feature, name the stakeholder, the benefit and
- the intended solution. A description in the user story format also helps people decide later whether a new scenario
+ To describe a feature, name the stakeholder, the benefit and
+ the intended solution.
+</p>
+<p>
+Describing features in the user-story format is a common practice, although they do not
+ necessarily map directly onto user stories. A description in the user-story format also helps people decide later whether a new scenario
belongs to a particular feature file or not.
<note>
A good strategy for describing a feature is to
- write the scenarios first, then summarise them and explain to someone who hasn't seen them before.
- Capture the explanation you used and put it into the header file. This will ensure that the
+ write the scenarios first, then summarise them and explain them to someone who hasn't seen them before.
+ Capture the explanation you used and put it into the Feature paragraph the file. This will ensure that the
feature file is self-explanatory.
</note>
</p>
@@ -236,26 +234,26 @@
<quote>Weaker opponent</quote>
and
<quote>Stronger opponent</quote>
- A scenario title provides a reader with the context required to understand the key example described by
- the scenario. A good strategy to use when deciding on a scenario title is to try to summarise the intent of
+ The title of a scenario should provide the context to enable the reader to understand the key example described by
+ the scenario. A good strategy to use when deciding on a scenario title is to try to summarise the purpose of
an example covered by the scenario to another person, and capture the phrase you used to explain
it.
</p>
<p>
Each scenario is described with one or more steps. In this example, the steps of the second
- scenario are:<quote><p>Given the ninja has a third level black-belt</p>
+ scenario are:<quote><p>Given the ninja has a third-level black belt</p>
<p>When attacked by Chuck Norris</p>
<p>Then the ninja should run for his life</p>
</quote>
</p>
<note title="Feature files are your katanas">
<p>Ninja always knows where his katana is. You should know the same for your feature files.
- They form a living documentation of a software system. They allow developers to
+ They form living documentation for a software system. They allow developers to
understand what needs to be implemented, testers to understand what is being automatically
checked and business users and support staff to quickly discover exactly what the system does.
They are not just test scripts. Feature files should be kept safe and secure as much as the
system code. When the code is branched, tagged or merged, these specifications should follow. The
- easiest way to do that is to keep them in the same version control system as the code.</p>
+ easiest way to do this is to keep them in the same version control system as the code.</p>
</note>
</section>
<section id="sec_good_feature_file" title="What makes a good feature file">
@@ -269,7 +267,7 @@
<li>Consistent</li>
<li>Easy to access</li>
</ul>
- <p>To serve as a target for development that prevents misunderstanding and ambiguities, scenarios
+ <p>To serve as a target for development that prevents misunderstanding and ambiguity, scenarios
in a feature file should be:</p>
<ul>
<li>Precise and testable</li>
@@ -279,7 +277,7 @@
<p>Once a feature is implemented, the related file becomes a living document for the feature.
To allow the software to evolve and provide functional regression checks, the scenarios should be:</p>
<ul>
- <li>Self explanatory</li>
+ <li>Self-explanatory</li>
<li>Focused</li>
<li>In domain language</li>
</ul>
@@ -299,25 +297,25 @@
illustrates an invalid number). Define what &lsquo;correctly&rsquo; means by illustrating it with
the precise output.</li>
<li>Inconsistent language: As the project evolves, the domain language might change. Cucumber
- feature files are your documentation, update them to be consistent with the new knowledge or people
+ feature files are your documentation, update them to be consistent with the updated language or people
won't be able to understand them later.</li>
<li>Several actions or mixed validations and actions: although a scenario that checks several
cases makes a perfectly good regression check, it is really hard to understand. Each scenario
- should be independent, focused on a single example and perform a single action. That will make it
- easier to understand the scenarios later. It also enables developers and testers to get quick
+ should be independent, focused on a single example and perform a single action. That will make them
+ easier to understand later. It also enables developers and testers to get quick
feedback from individual checks.</li>
<li>Generic feature explanations or titles of features and scenarios that do not explain the
- content of the file completely: Feature files should be self-explanatory. A good litmus-test for
+ content of the file completely. Feature files should be self-explanatory. A good litmus test for
this is to show the feature file to someone who hasn't seen it and ask them to explain it back to
- you. If they understand it differently than you, then the descriptions are wrong.</li>
+ you. If their understanding of it differs from yours, then the descriptions are wrong.</li>
</ul>
</section>
<section id="sec_collaborative_feature_files" title="Feature files should be written collaboratively">
<p>Many beginners fail with specification by example by expecting the business analysts or business users
- to write feature files. You can see people often complaining how Cucumber doesn't work for them because
+ to write feature files on their own. You can see people often complaining how Cucumber doesn't work for them because
their business analysts don't want to write feature files. This is a huge misunderstanding of the process
- and even if you can get them to write the files on their own, avoid doing that. Feature files should be
- a universally accepted definition of what done means for a particular story, from all the perspectives.
+ and you should avoid having them write the files on their own, even if they are willing. Feature files should be
+ a universally accepted definition of what makes a particular story complete, from all the perspectives.
That is why they need to be specified and written collaboratively. Everyone should contribute.
A good feature file contains the following key examples:
</p>
@@ -328,18 +326,18 @@
technical boundary conditions.
Developers will typically suggest such examples when they are concerned about functional gaps
or inconsistencies. Business users, analysts or customers will
- define the correct expected behavior.</li>
+ define the correct expected behaviour.</li>
<li>An example illustrating each particularly troublesome area of the expected implementation,
such as cases that caused bugs in the past and boundary conditions that might not be explicitly
illustrated by previous examples. Testers will typically suggest these and business
- users, analysts or customers will define the correct behavior.</li>
+ users, analysts or customers will define the correct behaviour.</li>
</ul>
</section>
<section id="sec_structuring_scenarios" title="Structuring scenarios">
<p>
Although there is no syntax check to prevent misuse, the steps in a scenario should follow the
<define>Given-When-Then</define>
- flow, which means that the entire context comes first, then an action, and then the expected
+ flow, which means that the preconditions come first, then an action, and then the expected
outcome of the action. Structuring scenarios like this ensures that they are focused and makes them
easier to understand.
</p>
@@ -347,7 +345,7 @@
<p>
A
<code>Given</code>
- step defines the preconditions for a scenario, similar to how Chunk Norris showing up is a precondition for a good fight. Technically, the step definition that automates it
+ step defines the preconditions for a scenario, such as Chunk Norris showing up being a precondition for a good fight. Technically, the step definition that automates it
should put the system into a known state for an automated test.
</p>
<p>
@@ -358,15 +356,16 @@
<code>Given a ninja is in the room</code>
<p>than</p>
<code>Given a ninja enters a room</code>
- <p> This distinction in the language might not seem so important initially, but it helps to focus
- readers' attention on the important activity of the scenario and avoid falling into the
+ <p> This distinction might not seem so important initially, but it helps to focus
+ readers' attention on the important action in the scenario and avoid falling into the
trap of scripting.</p>
</section>
<section title="When">
<p>
A
<code>When</code>
- step describes the primary action of a scenario &mdash; in case of Chuck Norris almost guaranteed to involve some kicking. Some good examples of the primary action are:
+ step describes the primary action of a scenario &ndash;
+in the case of Chuck Norris almost guaranteed to involve some kicking. Some good examples of the primary action are:
</p>
<ul>
<li>An activity of a user</li>
@@ -376,7 +375,7 @@
<p>
Try to write
<code>When</code> steps
- as actions, something that happens. This will focus the attention of the reader on that. It will
+ as actions, something that happens. This will focus the attention of the reader on the actions. It will
also help developers understand where to put the business logic and how to call the appropriate code
modules to implement the functionality.
</p>
@@ -393,11 +392,11 @@
</p>
<important title="But I want to verify internal changes">
<p>Verifying internal changes might be very important from a technical perspective, but the
- business users often couldn't care less about that. Use a technical testing tool (such as your
+ business users often couldn't care less about it. Use a technical tool (such as your
favourite unit testing tool) to specify and verify such things. You'll get a much faster
- turnaround from a technical perspective. You don't really need a test that is readable and
- accessible by business users for that, so Cucumber won't give you any real benefits for those
- checks.</p>
+ turnround from a technical perspective. You don't really need a test that is readable and
+ accessible by business users for these kinds of checks, so using Cucumber for them won't give you any real
+ benefits.</p>
</important>
</section>
<p>You can use three double-quote symbols (<code>"""</code>) to start and end a multi-line block. For example:
@@ -447,8 +446,8 @@
<section id="sec_internationalisation" title="Internationalisation">
<p>One of the biggest advantages of Cucumber over other literal automation tools is that it fully supports
internationalisation. You do not have to use keywords in English. You can write the feature file in around 40
- languages, including some very obscure such as LOLCATS and artificial languages such as Welsh. Here is,
- for example, the feature file from the introduction in French:</p>
+ languages, including some very obscure such as LOLCATS and artificial languages such as Welsh.
+ Here is, for example, the feature file from the introduction in French:</p>
<code>
Fonctionnalit&eacute;: Lutte ou de fuite
Afin d'augmenter le taux de survie ninja,
@@ -474,10 +473,11 @@ Plan du Sc&eacute;nario: ennemi plus fort
<section id="sec_gherkin_tag" title="Tagging for fun and profit">
<p>After a few months, scenarios and features in a project can become unwieldy. With hundreds of feature files, we might
-not want to run everything all the time. It is often useful to be able to quickly find or check just a subset of the features, for
-example skip running one or two very slow tests to get faster feedback. </p>
-<p>Cucumber allows us to manage large groups of scenarios easier with tags. Tags are free-form text comments assigned to a scenario. They
-provide meta-data about the type, nature or group of that scenario. You can assign tags to a scenario by putting it before the scenario header, adding
+not want to run everything all the time. It is often useful to be able to quickly find or check just a subset of the
+features, for
+example in order to omit one or two very slow tests to get faster feedback. </p>
+<p>Cucumber allows us to manage large groups of scenarios more easily with tags. Tags are free-form text comments assigned to a scenario. They
+provide meta-data about the type, nature or group of that scenario. You can assign a tag to a scenario by putting it before the scenario header, adding
an &lsquo;at&rsquo; sign (@) before the tag name. For example:</p>
<code>
@slowtest
@@ -486,25 +486,25 @@ an &lsquo;at&rsquo; sign (@) before the tag name. For example:</p>
</code>
<p>You can execute only the scenarios with a particular tag using the <code>--tags</code> option, for example:</p>
<code>cucumber --tags @fasttest</code>
-<p>Another option is to skip only the scenarios with a particular tag. For that, prefix the tag with a tilde (~). Here is an example:</p>
+<p>Another option is to skip the scenarios with a particular tag. For this, prefix the tag with a tilde (~). Here is an example:</p>
<code>cucumber --tags ~@slowtest</code>
<p>Here are some useful things you can do with tags:</p>
<ul>
-<li>Identify work in progress to avoid breaking the build when things like that fail.</li>
+<li>Identify work-in-progress to avoid breaking the build when newly introduced scenarios fail.</li>
<li>Separate quick and slow checks and execute as cascaded builds on the build server to speed up feedback.</li>
-<li>Execute some checks only overnight.</li>
-<li>Cross-reference stories and features/scenarios.</li>
-<li>Cross-reference entire feature areas and scenarios with cross-cutting concerns.</li>
+<li>Execute some checks overnight only.</li>
+<li>Cross-reference stories and features or scenarios.</li>
+<li>Link entire feature areas and scenarios that have cross-cutting issues.</li>
</ul>
-<p>You can tag a feature or a scenario (or outline). Feature tags are automatically inherited by all the enclosed scenarios and scenario outlines. Cucumber
-also supports some more advanced tag features, such as enforcing the limit on the number of scenarios with a particular tag (useful for in-progress work) and
+<p>You can tag a feature or a scenario. Feature tags are automatically inherited by all the enclosed scenarios and scenario outlines. Cucumber
+also supports some more advanced tag features, such as enforcing the limit on the number of scenarios with a particular tag (useful for work-in-progress) and
running scenarios that satisfy a combination of tags. For information on those options, have a look at
the <url link="https://github.com/aslakhellesoy/cucumber/wiki/tags" title="tags page on the Cucumber wiki"/>.</p>
</section>
-<section id="sec_gherkin_remember" title="Remember">
+<section title="Remember">
<ul>
-<li>Describe features from the perspective of users and stakeholders, not technical tasks</li>
+<li>Describe features from the perspectives of users and stakeholders, not software developers</li>
<li>Think of feature files as documentation when you write them.</li>
<li>Define the examples for feature files collaboratively.</li>
<li>Given steps describe the world as it was before an action. When steps describe activities that cause the world to change. Then steps specify how the world shuld change.</li>
View
144 plainbook/hello_dotnet.xml
@@ -13,10 +13,10 @@
</p>
</important>
<p>
- Download and run the one-click installer from the RubyInstaller web site.<footnote>
+ Download and run the one-click installer from the RubyInstaller website.<footnote>
<url link="http://rubyinstaller.org/" title="" />
</footnote>
- Although the latest version of Ruby at time of writing is 1.9.1, download the version 1.8.6. The
+ Download Ruby version 1.8.6, even though it is not the latest version. The
Cucumber .NET integration, called Cuke4Nuke, does not work out of the box with the latest version
of Ruby.
</p>
@@ -25,8 +25,7 @@
<code>bin</code>
directory, by default
<code>c:\Ruby\bin</code>, to your PATH environment variable.
- If you you are not sure how to do this, there are some good
- instructions in the Microsoft knowledge base.
+ If you are not sure how to do this, see the instructions in the Microsoft knowledge base.
<footnote>
<url link="http://support.microsoft.com/kb/310519" />
</footnote>
@@ -35,12 +34,12 @@
<p>Check your installation by running the following command from a command window:</p>
<code>ruby -v</code>
<p>If you set up
- everything properly and added the <code>bin</code> directory to your executable path, that command should
- print the version of Ruby you just installed, similar to the following:</p>
+ everything properly and added the <code>bin</code> directory to your executable path, this command should
+ print the version of Ruby you just installed, as in the following:</p>
<code>ruby 1.8.6 (2010-02-04 patchlevel 398) [i386-mingw32]</code>
<p> For full instructions
on installing a different version of Ruby, troubleshooting and advanced installation options, see the
- Ruby language web site.<footnote><url link="http://www.ruby-lang.org/en/downloads"/></footnote>
+ Ruby language website.<footnote><url link="http://www.ruby-lang.org/en/downloads"/></footnote>
</p>
</section>
@@ -51,29 +50,29 @@
Cucumber talks to .NET projects using a gem called Cuke4Nuke.<footnote><url link="http://github.com/richardlawrence/Cuke4Nuke" /></footnote>
(See <link ref="sidebar_gem" /> to
learn about gems).
- Cuke4Nuke sets up a TCP server which will accept Cucumber commands and execute them against .NET code. In the
+ Cuke4Nuke sets up a TCP server that accepts Cucumber commands and executes them against .NET code. In the
Cucumber jargon, this way of executing is called the <define>wire protocol</define>.
Cuke4Nuke allows you to use Cucumber for .NET projects without having any knowledge of Ruby.
</p>
<p>
- To install Cuke4Nuke, add GemCutter.org to the list of allowed Ruby Gem sources by executing the following command:
+ To install Cuke4Nuke, add GemCutter.org to the list of allowed Ruby gem sources by executing the following command:
</p>
<code>gem sources -a http://gemcutter.org/</code>
<p>
Then install the Cuke4Nuke gem by executing the following command from the terminal window:
</p>
<code>gem install cuke4nuke</code>
- <p>Finally, install the Win32Console gem to get colour reports, by using the following command:
+ <p>Finally, in order to view reports in colour, install the Win32Console gem using the following command:
</p>
<code>gem install win32console</code>
<p>
- Cucumber colours do not show up correctly on some versions of Windows. <url title="WAC" link="http://github.com/aslakhellesoy/wac" />
+ Cucumber colours do not show up correctly on some versions of Windows. <url title="WAC (Windows Ansi Colours)" link="http://github.com/aslakhellesoy/wac" />
is a workaround that
enables colour reporting by using ANSI colours. Download it from the following URL:
</p>
<code>http://github.com/aslakhellesoy/wac/raw/master/wac.exe</code>
<p>
- Put it somewhere on the disk, ideally in the executable path.
+ Put it somewhere on the hard drive, ideally in the executable path.
</p>
<sidebar id="sidebar_gem" title="Ruby Gems">
<p>
@@ -82,6 +81,7 @@
update and uninstall gems. To install a gem, use the following command:
</p>
<code>gem install gem_name</code>
+ <p>where <i>gem_name</i> is the name of the gem you are installing.</p>
<p>To download and install gems, RubyGems needs to know the addresses of one or more gem
repositories, called sources. You can view the currently configured sources using the following command:
</p>
@@ -95,7 +95,7 @@
</p>
<code>C:\Ruby\lib\ruby\gems\1.8\gems\</code>
<p>
- For more information on the gem package manager, see the list of options using the following command:
+ To see the list of options for the gem package manager, enter the following command:
</p>
<code>gem --help</code>
</sidebar>
@@ -106,14 +106,14 @@
</important>
<section id="sec_verifying_cuke4nuke" title="Verifying that Cuke4Nuke is installed correctly">
<p>
- To verify that the system is set up, execute
+ To verify that the system is installed correctly, execute
<code>Cuke4Nuke</code>
from the command line.
- You should see a help screen with Cuke4Nuke options. If you see an error that the command Cuke4Nuke isn't
+ You should see a help screen with Cuke4Nuke options. If you see an error report stating that the command Cuke4Nuke isn't
found, check the following:
<ul>
<li>There should be a <code>Cuke4Nuke.bat</code> executable in your Ruby <code>bin</code> folder. The path for
- this folder will be
+ this folder is
<code>C:\Ruby\bin</code>
if you accepted the default installation options for Ruby.
</li>
@@ -130,20 +130,13 @@
</section>
<note title="Cuke4Nuke uses NUnit assertions">
<p>Cuke4Nuke uses NUnit for assertions. Most .NET developers will have some version of NUnit installed, so we won't
- go into the details of that here. If you do not have it, go to <url link="http://nunit.org" /> and
- follow the installation instructions from that web site.
+ go into the details of this here. If you do not have it, go to <url link="http://nunit.org" /> and
+ follow the installation instructions from that website.
</p>
</note>
</section>
<section id="sec_hello_world_net" title="Hello World from .NET" >
- <important title="Cuke4Nuke does not like the 4.0 framework">
- <p>
- Your project needs to set the target framework as .NET Framework 3.5 if you are using VS2010. If you get an error message
- like the one below, then this is probably worth checking.
- </p>
- <code>Unable to contact the wire server at :3901. Is it up?</code>
- </important>
- <p>Let's go through a quick sample project to verify that the installation works.
+ <p>Let's go through a quick sample project to verify that the installation works.
Create a normal C# class library project (we called our example project <code>Cuke4NukeExample</code>).
Add a <code>Features</code> folder to it. This is where your Cucumber feature files will go.
Add a <code>step_definitions</code>
@@ -156,7 +149,7 @@
called <code>basic.feature</code> in the <code>Features</code> folder, with the following content:
</p>
<code file="dotnet/HelloCucumber/Cuke4NukeExample/features/basic.feature"/>
- <p>At this point, your project structure should look similar to the one in <link ref="fig.project_structure"/>.
+<p>At this point, your project structure should look similar to the one in <link ref="fig.project_structure"/>.
</p>
<img width="3in" src="project-structure.png" id="fig.project_structure" title="Cuke4Nuke Project structure"/>
<p>Now let's run Cuke4Nuke for the first time. Build the project, open a console window, go to your project
@@ -165,48 +158,59 @@
<code>Cuke4Nuke bin\debug\Cuke4NukeExample.dll</code>
<p>Of course, if you named your project differently, replace the DLL path accordingly.</p>
<p>You should see the Cucumber output telling you that there is one scenario with three steps, all of
- which are undefined. If you do not see the colours then add the <code>-c</code> flag and pipe the output to WAC.exe,
+ which are undefined. If you do not see the colours then add the <code>-c</code> flag and pipe
+ the output to wac.exe,
making the command similar to the following (replace the path to wac.exe with the folder where you saved it):
</p>
<code>Cuke4Nuke bin\debug\Cuke4NukeExample.dll -c | d:\apps\wac.exe</code>
- <p>The result should be similar to one in <link ref="fig.terminal_output"/>. As we still have
+ <important title="Cuke4Nuke does not like the .NET Framework 4.0">
+ <p>
+ Your project needs to set the target framework as .NET Framework 3.5 if you are using Visual Studio 2010. If you get an error message
+ like the one below, then this is probably worth checking.
+ </p>
+ <code>Unable to contact the wire server at :3901. Is it up?</code>
+ </important>
+<p>The result should be similar to one in <link ref="fig.terminal_output"/>. As we have still
not implemented any step definitions, Cuke4Nuke will not be able to match the feature file to
- any code. It therefore suggests some default step definitions. If these are in Ruby rather than C#, Cucumber
+ any code. It therefore suggests some default step definitions. If these are in Ruby rather than C#, it means that Cucumber
cannot find the <code>cucumber.wire</code> file. Make sure that it is in <code>Features\step_definitions</code>.</p>
<img id="fig.terminal_output" src="dn_terminal_output_1.PNG" title="Cuke4Nuke suggests steps"/>
<p>
Now let's add the step definitions that help Cucumber talk to our project code. Create a C# class
- file called <code>HelloWorldSteps.cs</code> and edit it so it looks like the example below:
+ file called <code>HelloWorldSteps.cs</code> with the following content:
</p>
- <code file="dotnet/HelloCucumber/Cuke4NukeExample/HelloWorldSteps.cs"/>
- <img src="dn_terminal_output_2.PNG" id="fig.terminal_output2" title="Cuke4Nuke executes our test"/>
- <p>
- Add a reference to NUnit.Framework.dll (from your NUnit installation) and Cuke4Nuke.Framework.dll
- (by default, it is installed into <code>C:\Ruby\lib\ruby\gems\1.8\gems\cuke4nuke-0.3.0\dotnet\</code>).
- Now build the project again, go to the console and re-run the Cuke4Nuke command. The output should
- now say that the steps passed, similar to the result in <link ref="fig.terminal_output2"/>.
- </p>
- <p>
- This is a very simplistic implementation for all the steps, that does not actually
+ <p>
+ This is a very simple implementation that does not actually
connect to any domain code. We go through all the details of the feature file and step definitions in <link ref="chp_feature_files" />. For now,
- you can probably guess that Cucumber uses regular expression in the attributes
+ you can probably guess that Cucumber uses regular expressions in the attributes
<code>Given</code>, <code>When</code> and <code>Then</code>
to map lines in a feature file to step definitions.
+
+ Our simple script sets the subject and the action and then verify the greeting using the
+ standard NUnit assertion <code>Assert.AreEqual</code>.</p>
+
+ <p>
+ Add a reference to NUnit.Framework.dll (from your NUnit installation) and Cuke4Nuke.Framework.dll
+ (by default, it is installed into <code>C:\Ruby\lib\ruby\gems\1.8\gems\cuke4nuke-0.3.0\dotnet\</code>).
+ Now build the project again and rerun the Cuke4Nuke command. The output should
+ now say that the steps passed, as in <link ref="fig.terminal_output2"/>.
</p>
- <p>Our simple script will set the subject and the action and then verify the greeting using the
- standard NUnit assertion <code>Assert.AreEqual</code>. Just to see it when it fails, modify the
+ <code file="dotnet/HelloCucumber/Cuke4NukeExample/HelloWorldSteps.cs"/>
+ <img src="dn_terminal_output_2.PNG" id="fig.terminal_output2" title="Cuke4Nuke executes our test"/>
+ <p>
+To see what hapens when it fails, modify the
<code>CheckGreeting</code>
method or feature source so that they don’t match and re-run Cuke4Nuke.
</p>
</section>
<important title="Cuke4Nuke keeps timing out">
- <p>Sometimes you might think that Cuke4Nuke has some special ninja invisibility skills, it just disappears. The reason
- for that is most likely that a step is taking too long to execute. Cucumber and Cuke4Nuke communicate using TCP and
+ <p>Sometimes you might think that Cuke4Nuke has some special ninja invisibility skills &ndash; it just disappears. The reason
+ is most likely that a step is taking too long to execute.
Cucumber only allows steps a very short time to execute by default. To increase the timeout, add a timeout section
- to your <code>cucumber.wire</code> file and an <code>invoke</code> subitem. Set the number of
- seconds allowed for a step to execute there. For example, the following file sets that to 200 seconds:</p>
+ to your <code>cucumber.wire</code> file and set the number of
+ seconds allowed for a step to execute in it. For example, the following file sets this value to 200 seconds:</p>
<code file="dotnet/NinjaSurvivalRate/features/step_definitions/cucumber.wire"/>
</important>
<section id="sec_net_build_integration" title="Build integration">
@@ -216,7 +220,7 @@
</p>
<code file="dotnet/HelloCucumber/Cuke4NukeExample/Cuke4NukeExample.csproj" part="PostBuildEvent"/>
<p>
- When you build the project, the output window should show that Cucumber tests are executed as well.
+ When you build the project, the output window should show that Cucumber tests have been executed as well.
<!--
similar to the output in <link ref="fig.dn.build_output" />.
-->
@@ -226,23 +230,12 @@
<img float="true" src="dn_build_output.PNG" id="fig.dn.build_output" title="Cuke4Nuke tests run in Visual Studio"/>
-->
</section>
- <section id="sec_net_ci" title="Continuous Integration">
+ <section id="sec_net_ci" title="Continuous integration">
<p>
To complete the project setup, let’s run Cucumber tests within a continuous integration environment and
store
test outputs next to project build results. We'll use TeamCity in this example.
</p>
- <note title="Cucumber and TeamCity">
- <p>
- Setting up TeamCity is outside the scope of this tutorial, but it is fairly easy to do. Grab it from
- <url link="http://jetbrains.com" />.
- TeamCity 5 should support Cucumber out of the box but we have not been able to make it work. TeamCity
- documentation suggests that a few environment variables should do the trick for ANT/Java builds, but this
- does not
- work for Cuke4Nuke. The Cucumber Teamcity template uses a deprecated API and fails to build with the latest
- Cucumber version.
- </p>
- </note>
<p>
Cucumber
can
@@ -251,13 +244,13 @@
the
test subfolder of our project folder, and tell TeamCity to monitor that. First, let’s change the .csproj
project file.
- Modify the <code>PropertyGroup</code> block you just added to the following:
+ Modify the <code>PropertyGroup</code> block you just added as follows:
</p>
<code file="dotnet/HelloCucumber/propertygroup_ci.txt"/>
<p>
- This <code>-f junit</code> option will tell Cucumber to save test results in the JUnit format, and
+ The <code>-f junit</code> option tells Cucumber to save test results in the JUnit format, and
<code>-o$(ProjectDir)test</code> tells it to save the files in the <code>test</code> subfolder of our project
- directory. The <code>PreBuildEvent</code> will delete any previous test results from that folder
+ directory. The <code>PreBuildEvent</code> deletes any previous test results from this folder
when the build starts.
</p>
<p>
@@ -274,26 +267,27 @@
</p>
<img width="2.5in" src="teamcity-results.png" id="img.dn.teamcity.results" title="TeamCity reporting test passes"/>
</section>
- <section title="Debugging Cuke4Nuke steps" id="sec_dotnet_debug">
+ <section title="Debugging Cuke4Nuke steps and feature files" id="sec_dotnet_debug">
<p>Because of the way that Cuke4Nuke and Cucumber run, it is virtually impossible to debug Cuke4Nuke steps properly
directly from Visual Studio, even if you set up the project to launch an external process for debugging. There
- are two workarounds that we use to debug steps: </p>
- <ol>
+ are two workarounds that we use for debugging: </p>
+ <ul>
<li>
- Call the step from a unit test, or even convert the step definition class to a unit test fixture class (Cuke4Nuke can
+ Call each step from a unit test, or even convert the step definition class to a unit test fixture class (Cuke4Nuke can
use NUnit test methods as step definitions as long as they have the correct attributes). This allows you
- to execute individual steps in isolation. If you find yourself doing this too much, this means that there is a lot of
- logic in the steps which probably hints at the need to push some of that logic into the domain code.
+ to execute individual steps in isolation. If you find yourself doing this frequently, this may mean that there
+ is a lot of
+ logic in the steps, and this may indicate that you should move some of the logic into the domain code.
</li>
- <li>The unit test approach only works for troubleshooting the logic inside a step, but it doesn't really allow you
- to debug a running feature file. For that, put <code>System.Diagnostics.Debugger.Break()</code> where
+ <li>
+ If you want to debug the entire feature file rather than individual steps, put <code>System.Diagnostics.Debugger.Break()</code> where
you want to set a breakpoint and run Cuke4Nuke normally. When the CLR hits the breakpoint, it will offer to
open up another Visual Studio instance and debug it. You will then be able to inspect any variables
- and step through the execution. Remember to increase the timeout for the wire protocol
+ and step through the execution. Remember to increase the timeout for the wire protocol,
otherwise Cucumber will kill Cuke4Nuke before the debugger launches. Also remember to delete or comment
out this line before committing the step definition files to the version control system, otherwise the continuous
- build process will start blocking and offering to debug the code as well.</li>
- </ol>
+ build process will be blocked when it hits the debug point and offering to debug the code as well.</li>
+ </ul>
</section>
</chapter>
View
168 plainbook/hello_java.xml
@@ -12,8 +12,7 @@
</p>
<important title="What is JRuby?">
<p>
- JRuby is a version of the Ruby runtime environment which runs in the JVM. It differs from other Ruby
- like JVM based languages such as Groovy in that it aims for complete compatibility with Ruby. At the time
+ JRuby is a version of the Ruby runtime environment which runs in the JVM. It differs from other Ruby-like JVM-based languages such as Groovy in that it aims for complete compatibility with Ruby. At the time
of writing JRuby is compatible with Ruby version 1.8.7.
</p>
</important>
@@ -27,7 +26,7 @@
default, it uses
<url link="http://www.picocontainer.org/" title="PicoContainer" />
to inject dependencies. If you already use Spring for application wiring on your project, you can
- use Spring instead of PicoContainer Cucumber automation as well. Add the following JVM argument when
+ use Spring instead of PicoContainer for Cucumber automation as well. Add the following JVM argument when
you run tests to tell Cuke4Duke to use Spring:<footnote>
For more information on the Spring integration, see
<url link="http://wiki.github.com/aslakhellesoy/cuke4duke/spring" />
@@ -36,16 +35,38 @@
<code>-Dcuke4duke.objectFactory=cuke4duke.internal.jvmclass.SpringFactory</code>
<p>
- In the rest of this chapter we are going to cover the three most common
- ways of using Cuke4Duke.
+ In the rest of this chapter we cover the three most common
+ ways of using Cuke4Duke:
<ul>
- <li><link ref="sec_jruby" /> covers installing JRuby and running Cuke4Duke from the terminal</li>
- <li><link ref="sec_ant"/> covers using Cuke4Duke with Ant</li>
- <li><link ref="sec_maven" /> covers using Cuke4Duke with Maven</li>
+ <li>running Cuke4Duke from the terminal; see <link ref="sec_jruby" /></li>
+ <li>using Cuke4Duke with Ant; see <link ref="sec_ant"/> </li>
+ <li>covers using Cuke4Duke with Maven; see <link ref="sec_maven" /> </li>
</ul>
- Then we will create a simple Hello-World project, and finally take a look at how to include Cucumber tests into
+ Then we will create a simple Hello World project, and finally take a look at how to include Cucumber tests into
a continuous build process.
</p>
+<important title="Speed up JRuby">
+<p>If the slow startup time of JRuby is giving you headaches, know that you can speed it up significantly by running a client VM. If you have a 64 bit operating system, you can also speed up the initial loading by running JRuby in 32 bit more. To do that, define the <code>JAVA_OPTS</code> environment variable and add <code>-client</code> and <code>-d32</code> to it. Here is the comparison of the same test run with and without these options:
+</p><code>
+$ time sh run.sh
+.....
+done
+
+real 0m19.438s
+user 0m26.235s
+sys 0m1.333s
+$ export JAVA_OPTS="-client -d32"
+$ time sh run.sh
+.....
+done
+
+real 0m7.455s
+user 0m8.088s
+sys 0m0.637s
+</code>
+<p>For more good tips on how to speed up JRuby, see <url link="http://blog.headius.com/2010/03/jruby-startup-time-tips.html" />.</p>
+</important>
+
<section id="sec_jruby" title="Using JRuby directly">
<p>
To run Cucumber features from the console without a build system such as Ant or Maven, you'll need
@@ -53,19 +74,18 @@
JRuby download page,<footnote>
<url link="http://jruby.org/download" />
</footnote>
- extract the contents into a directory and add the
+ extract the contents into a folder and add the
<code>bin</code>
- sub-directory of the JRuby installation to your PATH environment variable.
+ subfolder of the JRuby installation to your PATH environment variable.
</p>
- <important title="Maven (of course) downloads the Internet separately">
- <p>Don't install JRuby yourself if you intend to use Maven, jump to <link ref="sec_maven" /> and continue
- reading that. Maven will install JRuby in its own repository separately. </p>
+ <important title="Maven (of course) downloads the internet separately">
+ <p>Don't install JRuby yourself if you intend to use Maven; see <link ref="sec_maven" />. Maven installs JRuby in its own repository separately. </p>
</important>
<section title="Checking whether JRuby is installed correctly">
<p>Check if JRuby is installed correctly by running the following command from a terminal window:
</p>
<code>jruby -v</code>
- <p>If you set up JRuby correctly and added the <code>bin</code> folder to the executable path, you
+ <p>If you set up JRuby correctly and added the <code>bin</code> subfolder to the executable path, you
should see a message similar to the following:</p>
<code>
jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
@@ -74,19 +94,20 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
</section>
<section title="Installing the Cuke4Duke gem">
- <important title="Installing Gems behind a firewall">
- <p>
- If you do not have direct access to the internet, but instead go through a proxy server, you will have to set the HTTP_PROXY environment variable. Information about environment variables is available on the RubyGems site.<footnote><url link="http://docs.rubygems.org/read/chapter/12"/></footnote>
- </p>
- </important>
+
<p>
Install the Cuke4Duke gem for JRuby using the following command:
</p>
<code>jruby -S gem install cuke4duke</code>
<p>
Even if you have Cucumber installed in your stand-alone Ruby
- gem repository, you still need to install Cuke4Duke from JRuby.
- To test the installation, run the following command from a terminal window:
+ gem repository, you still need to install Cuke4Duke from JRuby. </p>
+<important title="Installing Gems behind a firewall">
+ <p>
+ If you do not have direct access to the internet, but instead go through a proxy server, you will have to set the HTTP_PROXY environment variable. Information about environment variables is available on the RubyGems site.<footnote><url link="http://docs.rubygems.org/read/chapter/12"/></footnote>
+ </p>
+ </important>
+ <p> To test the installation, run the following command from a terminal window:
</p>
<code>cuke4duke --help</code>
<p>This should print the available Cuke4Duke options.</p>
@@ -97,32 +118,32 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
</p>
<ul>
<li>
- The first is <code>--jars</code> which allows you to specify the location of library classes to include.
- Confusingly, this option does not accept a jar file, but instead expects a directory. You can specify this
- option several times to include multiple directories containing dependencies.
+ The first is <code>--jars</code>, which allows you to specify the location of library classes to include.
+ Confusingly, this option does not accept a jar file, but instead expects a folder. You can specify this
+ option several times to include multiple folders containing dependencies.
</li>
<li>
- The second additional option is <code>--require</code>, which you can use to specify the root directory for
+ The second additional option is <code>--require</code>, which you can use to specify the root folder for
your compiled classes for non-dynamic languages. For dynamic languages, put the step definitions in the
<code>features/step_definitions</code> folder and they will be included automatically.
</li>
</ul>
<p>For example, the following command will run all the feature files in the <code>features</code> folder,
- including all the JAR archives from the <code>lib</code> folder using the step
+ including all the JAR archives from the <code>lib</code> folder, using the step
definitions from the <code>target/test-classes</code> folder:</p>
<code>cuke4duke --jars lib --require target/test-classes features</code>
<p>
- If you write the step definitions in JRuby, then you can get the same result with the following command:
+ If you write the step definitions in JRuby, you can get the same result with the following command:
</p>
<code>cuke4duke --jars lib features</code>
</section>
<section id="sec_debugging_java" title="Debugging from a Java IDE">
- <p>The rest of this chapter describes how to run Cuke4Duke from ANT and Maven, which makes it also easy to debug. If you decide to
+ <p>If you run Cuke4Duke from ANT and Maven debugging is easy. However, if you decide to
use JRuby to run Cucumber features directly, debugging is a bit more complex. JRuby is a Java program, so instead of launching
Cuke4Duke directly, you can launch JRuby and pass the arguments you would pass from the command line. The main JRuby class is
- <code>org.jruby.Main</code>. For JRuby to run correctly, you should also define the JRuby root directory using
+ <code>org.jruby.Main</code>. For JRuby to run correctly, you should also define the JRuby root folder using
the <code>jruby.home</code> JVM variable. See <link ref="fig_cucumber_jruby_eclipse_1" /> and
- <link ref="fig_cucumber_jruby_eclipse_2" /> for an example of how to do that in Eclipse.</p>
+ <link ref="fig_cucumber_jruby_eclipse_2" /> for an example of how to do this in Eclipse.</p>
<img id="fig_cucumber_jruby_eclipse_1" src="cucumber-bare-eclipse-1.png" title="Running JRuby from Eclipse" />
<p>You can execute or debug this run configuration now as you would do any other Java program. Don't forget to add
@@ -146,7 +167,7 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
<ul>
<li><code>build.xml</code> is the ANT build file that specifies how you want to build and package the project</li>
<li><code>ivy.xml</code> specifies the required dependencies for the project.</li>
- <li><code>ivysettings.xml</code> tells Ivy where download the dependencies from.</li>
+ <li><code>ivysettings.xml</code> tells Ivy where to download the dependencies from.</li>
</ul>
<section title="Project build file">
<p>Create a new ANT build file in your project folder, call it <code>build.xml</code> and paste the
@@ -162,7 +183,7 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
</section>
<section title="Project dependencies">
<p>
- Create a file called <code>ivy.xml</code> in your project folder which instructs Ivy to download Cuke4Duke and its dependencies. It should have the following content:
+ Create a file called <code>ivy.xml</code> in your project folder. This instructs Ivy to download Cuke4Duke and its dependencies. It should have the following content:
</p>
<code file="java/HelloCucumber/ivy.xml"/>
</section>
@@ -179,10 +200,12 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
</section>
<section title="Using Maven" id="sec_maven">
<p>
- If you are using Maven, you will need to set up the <code>pom.xml</code> project file to include Cuke4Duke and install the required gems.
- and java dependencies. Setting up Maven is outside the scope of this tutorial, but you will find the full working example of the POM file on the companion web site of this book.<footnote><url link="http://cuke4ninja.com" /></footnote>
+ If you are using Maven, you will need to set up the <code>pom.xml</code> project file to
+include Cuke4Duke and install the required gems.
+ and java dependencies. Setting up Maven is outside the scope of this tutorial, but you will
+ find the full working example of the project file on the companion web site of this book.<footnote><url link="http://cuke4ninja.com" /></footnote>
</p>
- <section title="Setting up the POM file">
+ <section title="Setting up the project file">
<p>
Add the following repositories to the POM file:
</p>
@@ -213,49 +236,51 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
Cucumber tests using Maven, execute the following command line:</p>
<code>mvn clean integration-test</code>
<p>
- That command cleans the output folder, re-compiles the projects and
+ This command cleans the output folder, recompiles the projects and
then reruns the test. While this is the best way to ensure the tests are running against
the latest code base, it will take longer to run. If you are not making changes to classes,
you can run the test using the following command:
</p>
<code>mvn cuke4duke:cucumber</code>
- <note title="Installing gems">
- <p>The first time you run Cucumber with Maven, you will have to specify one additional arguments so that JRuby downloads
- and installs the required gems. Run Maven as:</p>
- <code>mvn integration-test -Dcucumber.installGems=true</code>
- <p>You only need to do this once on any particular system.</p>
- </note>
-
+
</section>
</section>
<!-- here -->
<section title="Hello world from Java" id="sec_java_helloworld">
<p>Let's go through a quick sample project to verify that the installation works. We will
use Maven to run the project. If you would prefer to use Ant or JRuby directly rather than Maven, then
- set up the project as described in the previous section. You can download the full project code
- for both Ant and Maven from the <url link="http://cuke4ninja.com" title="companion web site"/> of this book.
+ set up the project as described in the appropriate previous section. You can download the full project code
+ for both Ant and Maven from the <url link="http://cuke4ninja.com" title="companion website"/> of this book.
</p>
<section title="Project Setup">
<p>
- Set up a new project using the standard project layout and add a <code>features</code> sub-directory to the
- project directory. This is where your feature files will go. For a Maven project,
+ Set up a new project using the standard project layout and add a <code>features</code> subfolder to the
+ project folder. This is where your feature files will go. For a Maven project,
the final layout will be as in <link ref="fig.project-structure-java" />. Make sure that
- <code>src/test/java</code> is marked as a test source directory in your IDE.
+ <code>src/test/java</code> is marked as a test source folder in your IDE.
</p>
- <img src="project-structure-java.png" id="fig.project-structure-java" title="Maven project directory structure"/>
+ <img src="project-structure-java.png" id="fig.project-structure-java" title="Maven project folder structure"/>
</section>
<section title="Adding the feature file">
<p>Create a file in the <code>features</code> folder in your project called <code>basic.feature</code> and add the following content:
</p>
<code file="java/HelloCucumber/features/basic.feature"/>
- <p>Now run Cucumber. Open a terminal window and execute the following command from the project folder:
+ <p>Now run Cucumber: open a terminal window and execute the following command from the project folder:
</p>
<code>mvn integration-test -Dcucumber.installGems=true</code>
- <p>Of course, if you decided to use Ant or JRuby directly, use the commands described in the previous sections to execute the tests. </p>
+
+ <note title="Installing gems">
+ <p>The first time you run Cucumber with Maven, you will have to specify one additional argument so that JRuby downloads
+ and installs the required gems. Run Maven as:</p>
+ <code>mvn integration-test -Dcucumber.installGems=true</code>
+ <p>You only need to do this once on any particular system.</p>
+ </note>
+
+ <p>Of course, if you decided to use Ant or JRuby directly, use the commands described in the previous sections to execute the tests. </p>
<p>
- Near the end of the output of the maven build report in the terminal window you should see something like the result in <link ref="fig.j_terminal_output" />.
- Below that, Maven build result shows a suggested implementation for the step definitions, such as in <link ref="fig.j_terminal_output2"/>.
+ Near the end of the output of the Maven build report in the terminal window you should see something like the result in <link ref="fig.j_terminal_output" />.
+ Below that, the Maven build result shows a suggested implementation for the step definitions, such as the one shown in <link ref="fig.j_terminal_output2"/>.
This shows that Cuke4Duke is running and has identified the feature file.
As we have not yet created any step definitions, the test execution will fail. It will however do three
three important things:
@@ -274,23 +299,22 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
<section title="Writing step definitions">
<p>
- Now let's add the step definitions that help Cucumber talk to our project code. Add a class called
- <code>BasicFeature</code> into the <code>test/java/hellocucumber</code> directory, with the following content:
+ Now let's add the step definitions that help Cucumber talk to our project code. Add a class called
+ <code>BasicFeature</code> to the <code>test/java/hellocucumber</code> folder, with the following content:
</p>
<code file="java/HelloCucumber/src/test/java/hellocucumber/BasicFeature.java"/>
<p>
- Now build the project again and re-run Cuke4Duke, using the following command:
+ Now build the project again and rerun Cuke4Duke, using the following command:
</p>
<code>mvn clean integration-test</code>
<p>
- Note that we don't need the <code>-D</code> any more because the gems should now be installed. The
- output should now say that the steps passed, similar to the result in <link ref="fig.j_terminal_output_3"/>.
+ The output should now say that the steps passed, as shown in <link ref="fig.j_terminal_output_3"/>.
</p>
<p>
- This is a very simplistic implementation for all the steps, that does not actually
+ This is a very simple implementation that does not actually
connect to any domain code. We go through all the details of the feature file and step
definitions in <link ref="chp_feature_files" />. For now,
- you can probably guess that Cucumber uses regular expression in the attributes
+ you can probably guess that Cucumber uses regular expressions in the attributes
<code>Given</code>, <code>When</code> and <code>Then</code>
to map lines in a feature file to step definitions.
</p>
@@ -306,22 +330,22 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
<section id="sec_java_ci" title="Continuous integration">
<p>
- We are only going to cover setting up TeamCity with a Maven base project in detail here, but you should be able
- to configure other CI systems similarly.
+ We only cover setting up TeamCity with a Maven base project in detail here, but you should be able
+ to configure other continuous integration (CI) systems in a similar way.
</p>
<p>
Cucumber can write out the results of the tests in a format that your CI system can process.
- In <link ref="sec_maven" /> we used <code>cucumberArgs</code> to set configure the output format:
+ In <link ref="sec_maven" /> we used <code>cucumberArgs</code> to set the output format:
</p>
<code file="java/Examples/ci_cucumberArg.txt"/>
<p>
These arguments instructed Cucumber to output the test results in JUnit format to the
- <code>target/cucumber-reports</code> directory. When we ran Cucumber, it created a file like this:
+ <code>target/cucumber-reports</code> folder. When we ran Cucumber, it created a file like this:
</p>
<code file="java/Examples/TEST-basic.xml"/>
<p>
Most CI servers should be able to parse these results and display them.
- As an example, we are going to set up a
+ As an example, we will set up a
TeamCity job to run the <code>integration-test</code> target and report the results.
</p>
<section title="Setting up CI using TeamCity">
@@ -331,18 +355,20 @@ jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
</p>
<p>
Add the project you created in <url link="sec_java_helloworld"/> to TeamCity as a Maven 2 project.
- On page that configures the Build Runner, select &lsquo;Maven 2&rsquo; from the dropdown and enter
- <code>clean integration-test</code> as goals, as in <link ref="fig.j.ci.buildrunner" />.
+ On the page that configures the Build Runner, select &lsquo;Maven 2&rsquo; from the &lsquo;Build runner&rsquo; dropdown and enter
+ <code>clean integration-test</code> in the Goals field, as in <link ref="fig.j.ci.buildrunner" />.
Further down the page, set the command line parameters to <code>-Dcucumber.installGems=true</code>
as in <link ref="fig.j.ci.cmd.line.params" />.
</p>
<img src="j.ci.buildrunner.png" id="fig.j.ci.buildrunner" title="Selecting Maven2 as the build runner"/>
<img src="j.ci.cmd.line.params.png" id="fig.j.ci.cmd.line.params" title="Setting command line parameters so gems are downloaded"/>
<p>
- At the bottom of the page, in the &lsquo;Import data from XML&rsquo; section, select &lsquo;Ant JUnit&rsquo; as a report type to import and enter <code>target/cucumber-reports/*.xml</code>
- as the report path, as in <link ref="fig.j.ci.junit" />. That is about it! Save the page and scroll to the top, where you should see a run button. Once the
+ At the bottom of the page, select &lsquo;Ant JUnit&rsquo; form the &lsquo;Import data from XML&rsquo; dropdown as the report type to import, and
+ enter <code>target/cucumber-reports/*.xml</code>
+ as the report path, as in <link ref="fig.j.ci.junit" />. That is about it! Save the page and scroll to the top,
+ where you should see a run button. Once the
project is running, if you select the projects tab, you should see the build happening. This may take a while the first time,
- since maven in its usual fashion will insist on downloading many libraries.
+ since Maven in its usual fashion will insist on downloading many libraries.
</p>
<img src="j.ci.junit.png" id="fig.j.ci.junit" title="Setting up build report type"/>
<p>
View
40 plainbook/hello_ruby.xml
@@ -1,32 +1,31 @@
<!DOCTYPE chapter SYSTEM "../resources/plainbook/plainbook.dtd" >
<chapter title="Cucumber and Ruby" id="chp_hello_ruby">
<p>
- In this chapter, we cover setting up Cucumber for a Ruby project.
+ In this chapter we cover setting up Cucumber for a Ruby project.
</p>
<sidebar title="Do you have Ruby installed?">
<p>
- In this chapter, we assume you have already installed ruby on your system. If this is not the case, there is
+ In this chapter, we assume you have already installed Ruby on your system. If this is not the case, there is
more information on the <url link="http://www.ruby-lang.org/en/" title="Ruby site"/>.
</p>
<p>
If you are running Windows
- there are some notes on installing Ruby in <link ref="chp_hello_dotnet"/>
+ see the notes on installing Ruby in <link ref="chp_hello_dotnet"/>
</p>
</sidebar>
<section id="sec_ruby_install_cucumber" title="Installing Cucumber">
- <p>To automate feature files using Ruby you need to install the <code>cucumber</code> gem.
- Do that with the following command:
+ <p>To automate feature files using Ruby you need to install the <code>cucumber</code> gem. Do that with the following command:
</p>
<code>gem install cucumber</code>
- <important title="Installing Gems behind a firewall">
+ <important title="Installing gems behind a firewall">
<p>
- If you do not have direct access to the internet, but instead go through a proxy server, you will have to set the HTTP_PROXY environment variable. Information about environment variables is available on the RubyGems site.<footnote><url link="http://docs.rubygems.org/read/chapter/12"/></footnote>
- </p>
+If you only have internet access through a proxy server, you will have to set the HTTP_PROXY environment variable. Information about environment variables is available on the RubyGems site.<footnote><url link="http://docs.rubygems.org/read/chapter/12"/></footnote>
+</p>
</important>
<p>Check that Cucumber is installed by running the following command in the terminal window:</p>
<code>cucumber --version</code>
- <p>Cucumber should print out the version (0.9.4 at the time when we wrote this). </p>
+ <p>Cucumber should print out the installed version. </p>
<p>Cucumber uses
RSpec for assertions. If you do not already have
RSpec, run the following command to install it:
@@ -34,14 +33,15 @@
<code>gem install rspec</code>
<important title="Cucumber won't install!">
<p>
- Cucumber depends on a particular gherkin version. It will try to download it automatically, but
+ Cucumber depends on a particular version of the gherkin gem
+It will try to download it automatically, but
if you already have a later version then RubyGems gets confused and won't download the version
required by Cucumber. As a result you might get an error message similar to this one:
</p>
<code>ERROR: Error installing cucumber:
cucumber requires gherkin (~> 2.2.9, runtime)</code>
<p>
- To resolve this, install the correct gherkin gem version first:
+ To resolve this, install the correct version of gherkin first:
</p>
<code>gem install gherkin --version 2.2.9 </code>
<p> Or just uninstall the previous version of gherkin if you do not need it for other reasons, and then install the latest version:</p>
@@ -55,36 +55,36 @@
<p>Let's go through a quick sample project to verify that the installation works.
Create a directory for the project, for example <code>HelloCucumber</code>.
Create a <code>features</code> directory in it. This is where the feature files go.
- Create a new file in it, called <code>basic.feature</code>, with the following content:
+ Create a new file in the directory called <code>basic.feature</code>, with the following content:
</p>
<code file="ruby/HelloCucumber/features/basic.feature"/>
<p>Run <code>cucumber</code> from the command line in the main project directory.
- When it cannot match steps to step definitions, Cucumber prints out what it thinks
+ When Cucumber cannot match steps to step definitions, it prints out what it thinks
would be a good default setup for the steps. As we have not implemented any steps yet,
Cucumber
should print a suggestion similar to the one in <link ref="fig.ruby_suggestions" />.
</p>
<img src="ruby_suggestions.png" id="fig.ruby_suggestions" title="Cucumber suggests a default implementation for step definitions"/>
<p>
- Now create a sub-directory called <code>step_definitions</code> in the <code>features</code> directory. This
+ Now create a subdirectory called <code>step_definitions</code> in the <code>features</code> directory. This
is where the automation layer between the feature files and the domain code goes. Create a new file
- called <code>basic_steps.rb</code> in the <code>step_definitions</code> directory. You project structure should end up looking something like in <link ref="fig_ruby_project_structure" />.
+ called <code>basic_steps.rb</code> in the <code>step_definitions</code> directory. Your project structure should be as shown in<link ref="fig_ruby_project_structure" />.
Paste the following content into <code>basic_steps.rb</code>:
</p>
<code file="ruby/HelloCucumber/features/step_definitions/basic_steps.rb"/>
<p>
- This is a very simplistic implementation for all the steps, that does not actually
+ This is a very simplistic implementation that does not actually
connect to any domain code. We go through all the details of the feature file and step definitions in <link ref="chp_feature_files" />. For now,
- you can probably guess that Cucumber uses regular expression after the keywords <code>Given</code>, <code>When</code> and <code>Then</code>
+ you can probably guess that Cucumber uses regular expressions after the keywords <code>Given</code>, <code>When</code> and <code>Then</code>
to map lines in a feature file to step definitions.
</p>
<img id="fig_ruby_project_structure" src="ruby_project_structure.png" title="Ruby project folders"/>
<p>
Now we are ready to run the features. Run
<code>cucumber</code> again from the command line in your main project directory, and you should
- get a result similar to the one in <link ref="fig_ruby_test_results" />. This tells us the number of
- features and steps executed, the lines of the step definitions, the steps
- executed and reports successful and failed test executions.</p>
+ get a result similar to the one shown in <link ref="fig_ruby_test_results" />. This tells us the number of
+ features and steps executed, the lines of the step definitions and the steps
+ executed, and reports successful and failed test executions.</p>
<img id="fig_ruby_test_results" title="Our feature works in Ruby" src="ruby_test_results.png"/>
</section>
</chapter>
View
234 plainbook/implementing.xml
@@ -1,24 +1,24 @@
<!DOCTYPE chapter SYSTEM "../resources/plainbook/plainbook.dtd" >
<chapter id="chp_implementing" title="Implementing the steps">
<p>To give aspiring Cucumber ninjas more examples to copy and
- paste while they are practicing their
+ paste while they are practising their
new skills, we now implement step definitions
from <link ref="chp_feature_files" />. We present the most important
snippets of the code to help you focus on the key parts. You can download the entire
source code with all the additional project files from <url link="http://cuke4ninja.com"/>.</p>
- <section id="sec_basic_feature_file" title="The basic feature file &mdash; again">
+ <section id="sec_basic_feature_file" title="The basic feature file &ndash; again">
<p>
- Create a new directory for the project, and add two sub-directories to it:
+ Create a new folder for the project, and add two subfolders to it:
<code>features</code>
and
- <code>src</code>. The feature files, unsurprisingly, go to the <code>features</code> subdirectory.
- We use the <code>src</code> subdirectory for the implementation.</p>
+ <code>src</code>. The feature files, unsurprisingly, go in the <code>features</code> subfolder.
+ We use the <code>src</code> subfolder for the implementation.</p>
<p>Create a new file in the
- <code>features</code>
+ <code>features</code> folder
(for example,
<code>ninja_survival_rate.feature</code>) with the following content:
</p>
- <code file="ruby/NinjaSurvivalRate/features/ninja_survival_rate.feature"/>
+ <code file="ruby/NinjaSurvivalRate/features/ninja_survival_rate.feature"/>
</section>
@@ -26,7 +26,7 @@
<section id="sec_steps_and_regex" title="Steps and regular expressions">
<p>Cucumber uses regular expressions to match steps in the feature file to step definitions in code.
We really need only three
- step definitions &mdash; one that sets the ninja belt level at the start, one that captures
+ step definitions &ndash; one that sets the ninja belt level at the start, one that captures
what the ninja does when attacked by an opponent and one that checks if the specified
action was in that list of activities. </p>
@@ -35,43 +35,44 @@
Regular expressions are a way to specify patterns of text. If you have never used them before, here are
just a few basic rules to follow. </p>
<ul>
- <li>Letters and numbers have literal values. The <code>ninja</code> pattern only matches
- the exact lowercase sequence n,i,n,j,a. </li>
- <li>Square brackets define groups of interchangeable elements. The whole group will match a single character. So <code>[Nn]inja</code> matches either
- n,i,n,j,a or N,i,n,j,a but not N,n,i,n,j,a. </li>
+ <li>Letters and numbers have literal values. The <code>ninja</code> pattern matches only
+ the exact lower-case sequence n,i,n,j,a </li>
+ <li>Square brackets define groups of interchangeable elements. The whole group can match only a single character. So <code>[Nn]inja</code> matches either
+ n,i,n,j,a or N,i,n,j,a but not N,n,i,n,j,a </li>
<li>An asterisk turns the character or group immediately before it to a sequence that repeats zero or more times.
- So <code>Nin*ja</code> will match N,i,n,j,a but also N,i,j,a and
- N,i,n,n,n,n,j,a.</li>
+ So <code>Nin*ja</code> matches N,i,n,j,a but also N,i,j,a and
+ N,i,n,n,n,n,j,a</li>
<li>A question mark turns the character or group immediately before it into a sequence that appears zero or one time. So <code>N?inja</code>
- will match N,i,n,j,a and i,n,j,a, but not N,N,i,n,j,a</li>
- <li>A dash defines a range. For example, <code>ninja1-9</code> will match n,i,n,j,a,1 but also
- n,i,n,j,a,2 and n,i,n,j,a,3 to n,i,n,j,a,9. <!-- Ranges work on ASCII/UTF code values, so numbers come before uppercase letters, which come before lowercase
+ matches N,i,n,j,a and i,n,j,a, but not N,N,i,n,j,a</li>
+ <li>A dash defines a range. For example, <code>ninja1-9</code> matches n,i,n,j,a,1 but also
+ n,i,n,j,a,2 and so on up to n,i,n,j,a,9 <!-- Ranges work on ASCII/UTF code values, so numbers come before uppercase letters, which come before lowercase
letters. Non-english letters might not be in the same sequence that they are in local alphabets. -->
</li>
- <li>A dot (<code>.</code>) matches any character. So <code>Ninj.</code> will match any sequence of five characters
- starting with N,i,n,j.</li>
+ <li>A dot (<code>.</code>) matches any character. So <code>Ninj.</code> matches N,i,n,j,x and any other sequence of five characters
+ starting with N,i,n,j</li>
<li>A backslash <code>\</code> turns a special character into a literal value. For example, <code>1\-9</code>
- matches only the sequence 1,-,9, not a number between 1 and 9.</li>
- <li><code>\s</code> matches a whitespace (blank).</li>
- <li>You can mix and match ranges, asterisks, letters...</li>
- <li>A caret (<code>^</code>) matches the beginning of the line. A dollar sign (<code>$</code>) matches the end of the line.</li>
- <li>Parenthesis () mark parts of an expression that is captured for later processing.</li>
+ matches only the sequence 1,-,9, not a number between 1 and 9</li>
+ <li><code>\s</code> matches a whitespace (blank)</li>
+ <li>You can mix and match ranges, asterisks, letters and so on</li>
+ <li>A caret (<code>^</code>) matches the beginning of the line. A dollar sign (<code>$</code>) matches the end of the line</li>
+ <li>Parentheses () mark parts of an expression that are captured for later processing.</li>
</ul>
</sidebar>
<section id="sec_initialising_context" title="Initialising the context">
- <p>To set the black belt level, we create a step definition which matches the lines such as:
- <quote>Given the ninja has a third level black-belt</quote>
- and capture the word between &lsquo;a&rsquo; and &lsquo;level&rsquo; as an argument. We can use a dash
+ <p>To set the black-belt level, we create a step definition that matches the line:
+ <quote>Given the ninja has a third-level black belt</quote>
+ and captures the word between &lsquo;a&rsquo; and &lsquo;level&rsquo; as an argument. We can use a dash
to define a range of characters. So
- <code>a-z</code> will match any lowercase letter. An asterisk after any character says
- that we expect a sequence. We use square brackets to apply an asterisk to the entire range, not just the
- letter <code>z</code>.
+ <code>a-z</code> will match any lower-case letter. An asterisk after any character says
+ that we expect a sequence. We use square brackets to apply an asterisk to the entire range, not
+just the previous
+ letter.
We add brackets around the expression to tell Cucumber
to capture the word as an argument and pass it on. So the full expression we want to use for the
match is:
</p>
- <code>^the ninja has a ([a-z]*) level black\-belt$</code>
- <note title="Double-escape in Java">
+ <code>^the ninja has a ([a-z]*)\-level black belt$</code>
+ <note title="Double escape in Java">
<p>The backslash <code>\</code> in a regular expression escapes
the following character. In this case, we use a backslash to tell Cucumber that a dash
is literally a dash, not a special range definition symbol such as in <code>a-z</code>. Ruby is OK
@@ -79,30 +80,34 @@
following character, so you'll have to use two backslashes <code>\\</code> to write it. So the correct
way to specify this in Java and .NET is:
</p>
- <code>^the ninja has a ([a-z]*) level black\\-belt$</code>
+ <code>^the ninja has a ([a-z]*)\\-level black belt$</code>
</note>
</section>
<section title="Triggering the action">
- <p>To trigger the action in both scenarios, we create a step definition which matches the lines such as:
+ <p>To trigger the action in both scenarios, we create a step definition that matches the lines:
<quote>When attacked by a samurai</quote>
and
<quote>When attacked by Chuck Norris</quote>
- To do that, we want to capture everything after &lsquo;attacked by&rsquo; as the opponent, ignoring &lsquo;a&rsquo; and a space
- if they follow straight after that. The expression <code>[a\s]</code> will match the lowercase letter <code>a</code> or a space
+ To do this, we need to capture everything after &lsquo;attacked by&rsquo; as the opponent, ignoring &lsquo;a&rsquo; and a space
+ if they follow straight after. The expression <code>[a\s]</code> will match the lower-case letter <code>a</code> or a space
(matched by a special sequence <code>\s</code>).
- We again add an asterisk to say that we expect any sequence of spaces or letter <code>a</code>, including an empty one. We don't add brackets
- around this expression because we want Cucumber to just ignore it. After that we want to capture anything till the end
- of the line. A dot matches any character, an asterisk turns this into a sequence
- and we add a dollar sign at the end of the expression <code>$</code>, telling Cucumber that the sequence
- should extend till the end of the current line. We want Cucumber to capture the sequence but not the line-end character,
+ We again add an asterisk to say that we expect any sequence of spaces or the letter <code>a</code>, including
+ nothing. We don't add brackets
+ around this expression because we want Cucumber to just ignore it. After this we want to capture anything up uo
+the end
+ of the line. A dot matches any character, an asterisk says there could be more than one character,
+ and we add a dollar sign <code>$</code> at the end of the expression to tell Cucumber that the sequence
+ should extend to the end of the current line. We want Cucumber to capture the sequence but not the line-end character,
so we close the bracket before the dollar sign. So the full regular expression for the action step is: </p>
<code>^attacked by [a\s]*(.*)$</code>
</section>
<section title="Validating the result">
<p>
- To validate the result, we create a step definition which matches the lines such as:
- <quote>Then the ninja should engage the opponent</quote> We capture anything that follows &lsquo;should engage&rsquo;
+ To validate the result, we create a step definition that matches the line:
+ <quote>Then the ninja should engage the opponent</quote>
+ or any other actions that the ninja should take, for example run away.
+ We capture anything that follows &lsquo;should engage&rsquo;
using the same expression as in the previous step, matching everything from
a certain position till the end of the line <code>(.*)$</code>. The full expression in this case is:</p>
<code>^the ninja should (.*)$</code>
@@ -118,33 +123,34 @@
</ul>
<section id="sec_ninja_survival_ruby" title="Implementing in Ruby">
<p>
- Create a new sub-directory called
+ Create a new subfolder called
<code>step_definitions</code>
inside
- <code>features</code>. You can run
+ <code>features</code>. Run
<code>cucumber</code>
- from the main project directory and it will print out an example of how the steps could be
- implemented. Take that and put it into a new file called
+ from the main project folder; it will display on screen an example of how the steps could be
+ implemented. Copy and paste this into a new file called
<code>ninja_steps.rb</code>, and replace the regular expressions with the ones we
suggested in the previous section.
</p>
<important title="Do I always create a steps file for a feature file?">
- Not necessarily. Cucumber will try to load steps from any step definition file when it executes a feature file. For
- convenience, it is often useful to keep related steps together, but you can even share state across different step
+ Not necessarily. Cucumber will try to load steps from any step definition file within the <code>step_definitions</code>
+ when it executes a feature file. For
+ convenience, it is often useful to keep related steps together, but you can share context across different step
definition files. See <link ref="sec_sharing_context" /> for more information.
</important>
<section title="Initializing the context">
- <p>We just need pass the argument captured by the step information
+ <p>We just need to pass the argument captured by the step information
to the initialiser of the <code>Ninja</code> class, and store the resulting <code>Ninja</code>
- object as a local variable. In Ruby, one way to do it is this:</p>
+ object as a local variable. In Ruby, one way to do this is:</p>
<code file="ruby/NinjaSurvivalRate/features/step_definitions/ninja_steps.rb" part="belt" />
- <important title="Wow &mdash; where did that class and initialiser come from?">
+ <important title="Wow &ndash; where did that class and initialiser come from?">
<p>If you're reading this carefully, you probably noticed that this is the first time we used a <code>Ninja</code>
- class and presumed what the arguments of its initializer are. Don't scroll up looking for the class definition, because it
+ class and assumed what the arguments of its initialiser are. Don't scroll up looking for the class definition, because it
doesn't exist yet. We will drive the class interface from its usage, so we first declare what we need that class to
do in the step definitions and then implement the class to match the required interface and behaviour. BDD takes the
- test-first ideas of TDD to the business requirements level.</p>
+ test-first ideas of test-driven development (TDD) to the business requirements level.</p>
</important>
</section>
<section title="Triggering the action">
@@ -165,7 +171,7 @@
<important title="What is that &lsquo;should include&rsquo; syntax?">
<p>Cucumber in Ruby relies on RSpec expectations to perform validations of expected outcomes.
<code>should include</code> is a way to specify that a collection should contain an element with RSpec. See
- <url link="http://rspec.info/"/> or the RSpec Book <bib ref="chelimsky:rspec" /> for more information
+ <url link="http://rspec.info/"/> or <i>The RSpec Book</i><bib ref="chelimsky:rspec" /> for more information
on the RSpec syntax.</p>
</important>
</section>
@@ -176,7 +182,7 @@
<code>ninja.rb</code>
in the
<code>src</code>
- directory with the following content:
+ folder with the following content:
</p>
<code file="ruby/NinjaSurvivalRate/src/ninja.rb" />
<p>
@@ -191,32 +197,32 @@
<section id="sec_ninja_survival_java" title="Implementing in Java">
<p>In this example, we use Maven to manage the dependencies, as SMERSH controls a stake in the world's biggest
internet providers and Maven finances our drug-smuggling operations by downloading unnecessary libraries
- every time you run it. Create a pom.xml for your project as explained in <link ref="sec_maven" />, and run
+ every time it is run. Create a pom.xml for your project as explained in <link ref="sec_maven" />, and run
the following command to get a suggested structure for step definitions:</p>
<code>mvn clean compile integration-test</code>
<note title="Remember to download the gems the first time you run Cucumber from Maven">
<p>When you run Cucumber with Maven for the first time, remember to add <code>-Dcucumber.installGems=true</code>.
- If you do not do that, Maven won't install cuke4duke and all the dependent gems in the local repository, and
+ If you do not do this, Maven won't install cuke4duke and all the dependent gems in the local repository, and
we won't get our money from force-feeding bandwidth. </p>
</note>
<p>Put the suggested expressions into a new class in the <code>src/test</code>
- branch. In the example, we use <code>src/test/java/ninjasurvivalrate/NinjaSurvivalSteps.java</code>. Now let's
+ folder. In the example, we use <code>src/test/java/ninjasurvivalrate/NinjaSurvivalSteps.java</code>. Now let's
change the expressions to the one we defined earlier.</p>
<important title="Do I always create a step class for a feature file?">
- Not necessarily. Cucumber will try to load steps from any step definition class when it executes a feature file. For
+ Not necessarily. Cucumber will try to load steps from any step definition class in the class path when it executes a feature file. For
convenience, it is often useful to keep related steps together, but you can even share state across different step
definition classes. See <link ref="sec_sharing_context" /> for more information.
</important>
<section title="Initializing the context">
- <p>We just need pass the argument captured by the step information
+ <p>We just need to pass the argument captured by the step information
to the constructor of the <code>Ninja</code> class, and store the resulting <code>Ninja</code>
object as a local variable. In Java, one way to do it is this:</p>
<code file="java/NinjaSurvivalRate/src/test/java/ninjasurvivalrate/NinjaSurvivalSteps.java" part="belt" />
- <important title="Wow &mdash; where did that class and constructor come from?">
+ <important title="Wow &ndash; where did that class and constructor come from?">
<p>If you're reading this carefully, you probably noticed that this is the first time we used a <code>Ninja</code>
- class and presumed what the arguments of its constructor are. Don't scroll up looking for the class definition, because it
+ class and assumed what the arguments of its constructor are. Don't scroll up looking for the class definition, because it
doesn't exist yet. We will drive the class interface from its usage, so we first declare what we need that class to
do in the step definitions and then implement the class to match the required interface and behaviour. BDD takes the
test-first ideas of TDD to the business requirements level.</p>
@@ -229,7 +235,7 @@
the actions a ninja
should take when attacked by a particular opponent. We'll store them in a local variable for later inspection.
</p>
- <note title="Remember the double-escape">
+ <note title="Remember the double escape">
<p>To specify a backslash in Java, you have to write it twice (\\).</p></note>
<code file="java/NinjaSurvivalRate/src/test/java/ninjasurvivalrate/NinjaSurvivalSteps.java" part="attacked" />
</section>
@@ -243,7 +249,7 @@
<section title="Implementing the domain code">
<p>
We still don't have a Ninja class to handle the calls from the steps, so let's add it. Create it in the
- <code>src/main</code> branch. In this example, we use <code>src/main/java/ninjasurvivalrate/Ninja.java</code>.
+ <code>src/main</code> directory. In this example, we use <code>src/main/java/ninjasurvivalrate/Ninja.java</code>.
The content is relatively straightforward:
</p>
@@ -261,7 +267,7 @@
<p>
Create a .NET project and put the feature file into it, in the <code>features</code> folder. Create
- a new sub-directory called
+ a new subfolder called
<code>step_definitions</code>
inside
<code>features</code> and add a <code>cucumber.wire</code> file with the following content:
@@ -269,27 +275,27 @@
<code file="dotnet/HelloCucumber/Cuke4NukeExample/features/step_definitions/cucumber.wire"/>
<p>This will set up the Cuke4Nuke integration with Cucumber. </p>
<p>
- You can now run
+ Now run
<code>cuke4nuke</code>
- from the main project directory and it will print out an example of how the steps could be
- implemented. Take that and put it into a new source file called
+ from the main project folder; it will display on screen an example of how the steps could be
+ implemented. Copy that and paste it into a new source file called
<code>NinjaSteps.cs</code>. Replace the regular expressions with the ones we
suggested in the previous section.
</p>
<important title="Do I always create a step class for a feature file?">
- Not necessarily. Cucumber will try to load steps from any step definition class when it executes a feature file. For
+ Not necessarily. Cucumber will try to load steps from any step definition class in the class path when it executes a feature file. For
convenience, it is often useful to keep related steps together, but you can even share state across different step
definition classes. See <link ref="sec_sharing_context" /> for more information.
</important>
- <section title="Initializing the context">
- <p>We just need pass the argument captured by the step information
+ <section title="Initialising the context">
+ <p>We just need to pass the argument captured by the step information
to the initialiser of the <code>Ninja</code> class, and store the resulting <code>Ninja</code>
- object as a local variable. In .NET, one way to do it is this:</p>
+ object as a local variable. In .NET, one way to do this is:</p>
<code file="dotnet/NinjaSurvivalRate/src/NinjaSteps.cs" part="belt" />
- <important title="Wow &mdash; where did that class and constructor come from?">
+ <important title="Wow &ndash; where did that class and constructor come from?">
<p>If you're reading this carefully, you probably noticed that this is the first time we used a <code>Ninja</code>
- class and presumed what the arguments of its constructor are. Don't scroll up looking for the class definition,
+ class and assumed what the arguments of its constructor are. Don't scroll up looking for the class definition,
because it
doesn't exist yet. We will drive the class interface from its usage, so we first declare what we need that class to
do in the step definitions and then implement the class to match the required interface and behaviour. BDD takes the
@@ -328,21 +334,21 @@
</section>
<section title="Sharing context across step definition files" id="sec_sharing_context">
<p>Putting all our ninjas in a single file is a good trick, made popular by the Black Star Ninja in the famous documentary about the
- life of Ninjas on Philippines from the 80's, wrongly titled The American Ninja. However, sometimes it is useful to break our step definitions
+ life of ninjas on the Philippines from the 80s, wrongly entitled <i>The American Ninja</i>. However, sometimes it is useful to break our step definitions
into several files, and then share the context between them. Cucumber supports this with all the platforms, but in different ways.</p>
<p>So far, all our steps have been in a single file, corresponding to a feature file. As things get more complex, you'll want to write feature
files that reuse some of the steps from other feature files and combine them with other steps, so the mapping between a step definition and
a feature file is not necessarily the same as between Subzero and Scorpion. In this section, we'll show aspiring Ninja Cucumberists how to
split these apart.</p>
- <p>To demonstrate that, let's add a new feature file that reuses some of the steps that we already defined and adds some new steps. Paste the
- following content in <code>features/split.feature</code>:</p>
+ <p>To demonstrate this, let's add a new feature file that reuses some of the steps that we already defined and adds some new steps. Paste the
+ following content into <code>features/split.feature</code>:</p>
<code file="ruby/NinjaSurvivalRate/features/split.feature" />
<p>The initial step <code>Given the ninja has a third level black-belt</code> will be already implemented. This step previously created a
<code>Ninja</code> object that we'll share with another step definition file.</p>
<section title="Sharing context in Ruby" id="sec_sharing_context_ruby">
- <p>Ruby loads step definitions from any files in the <code>step_definitions</code> directory. They do not have to all be in a single file.
- To see that, run the following command line:</p>
+ <p>Ruby loads step definitions from any files in the <code>step_definitions</code> folder. They do not all have to be in a single file.
+ To see this, run the following command line:</p>
<code>cucumber features/split.feature</code>
<p> Cucumber will execute just the new feature and report that it found the first step in <code>ninja_steps.rb</code> but that the other steps
are still undefined (see <link ref="img_ruby_split" />).</p>
@@ -354,52 +360,64 @@
</p>
<code file="ruby/NinjaSurvivalRate/features/step_definitions/split_steps.rb" />
<p>We'll leave the ImpactCalculator class to you for homework, or if you want to cheat go and get it from the
- <url link="http://github.com/davedf/cuke4ninja" title="Cuke4Ninja github repository" /> - look for a file called <code>impact_calculator.rb</code>.</p>
- </section>
-
- <section title="Sharing context in Java" id="sec_sharing_context_java">
- <p>Cuke4Duke loads step definitions from any classes annotated with Cucumber annotations such as <code>@Given</code>, they to not have to be all in the same file. To see that, run your tests again after adding a new feature file. For example, if running with maven, run the following command line:</p>
- <code>mvn integration-test</code>
- <p>Cucumber will execute the new feature file and find some of the steps in the old <code>NinjaSkillSteps.java</code> step definition class, but report other steps as still undefined (see <link ref="img_java_split" />).</p>
- <img src="split-java.png" id="img_java_split" title="Cuke4Duke finds some split ninja steps" />
- <p>The first step, from <code>NinjaSkillSteps.java</code>, creates an internal Ninja object. Because we want to be able to reuse that
- object in two different step definition classes, we have to enable another step definition class to somehow see that state.
- Before you even think about saying &lsquo;static&rsquo; know that Chuck Norris moves static objects. Remember that you had to add PicoContainer or Spring to the classpath? Now you'll finally understand why. Cuke4Duke shares state between step definitions using dependency injection. We can just create a class to hold the context required for our steps and add an instance of that class as a constructor argument for step definition classes. Cuke4Duke will ensure that both classes get the same context. Let's do that. </p>
- <p>First, let's define a context class. We just need to share the <code>Ninja</code> object so far, so let's create a container for a ninja:</p>
- <code file="java/NinjaSurvivalRateWithSharedState/src/test/java/ninjasurvivalrate/NinjaContext.java"/>
- <p>Now let's change the existing <code>NinjaSurvivalSteps.java</code> to expect an instance of this new class in the constructor:</p>
- <code part="constructor" file="java/NinjaSurvivalRateWithSharedState/src/test/java/ninjasurvivalrate/NinjaSurvivalSteps.java" />
- <p>At this point, you should be able to run the tests again and not notice any difference in the old functionality. Although the old step definition class did not have a constructor and this one does, and it requires a context parameter, Cuke4Duke wires that in automatically using your selected dependency injection container</p>
- <p>Now we can add another step definition class with a similar constructor to handle the missing steps:</p>
- <code file="java/NinjaSurvivalRateWithSharedState/src/test/java/ninjasurvivalrate/SplitSteps.java" />
- <p>We'll leave it to you for homework to implement the missing function for impact analysis in the <code>Ninja</code> class and
- to change the rest of the <code>NinjaSurvivalSteps.java</code> to use the context Ninja instead of the private one. If you want to cheat or admit that you are lazy, look for a folder called <code>NinjaSurvivalRateWithSharedState</code> in the <url link="http://github.com/davedf/cuke4ninja" title="Cuke4Ninja code repository" />.</p>
+ <url link="http://github.com/davedf/cuke4ninja" title="Cuke4Ninja github repository" /> &ndash; look for a file called <code>impact_calculator.rb</code>.</p>
</section>
<section title="Sharing context in .NET" id="sec_sharing_context_net">
- <p>Cuke4Nuke loads step definitions from any classes annotated with Cucumber annotations such as <code>[Given]</code>,
- they to not have to be all in the same file. To see that, run your tests again after adding a new feature file. </p>
+ <p>Cuke4Nuke loads step definitions from any classes annotated with Cucumber annotations such as
+<code>[Given]</code>,
+ they do not all have to be in the same file. To see this, run your tests again after adding a the feature file <code>split.feature</code>. </p>
<p>Cucumber will execute the new feature file and find some of the steps in the old <code>NinjaSteps.cs</code> step definition class,
but report other steps as still undefined.</p>
- <p>The first step, from <code>NinjaSteps.cs</code>, creates an internal Ninja object. Because we want to be able to reuse that
+ <p>The first step, from <code>NinjaSteps.cs</code>, creates an internal <code>Ninja</code> object.
+Because we want to be able to reuse that
object in two different step definition classes, we have to enable another step definition class to somehow see that state.
Before you even think about saying &lsquo;static&rsquo; know that Chuck Norris moves static objects.
- Cuke4Nuke shares state between step definitions using dependency injection. We can just create a class to hold the context
+ Cuke4Nuke shares context between step definitions using dependency injection. We can just create a class to hold the context
required for our steps and add an instance of that class as a constructor argument for step definition classes. Cuke4Nuke will ensure
- that both classes get the same context. Let's do that. </p>
+ that both classes get the same context.</p>
<p>First, let's define a context class. We just need to share the <code>Ninja</code> object so far, so let's create a container for a ninja:</p>
<code file="dotnet/NinjaSurvivalRateWithSharedState/src/NinjaContext.cs"/>
<p>Now let's change the existing <code>NinjaSteps.cs</code> to expect an instance of this new class in the constructor:</p>
<code part="constructor" file="dotnet/NinjaSurvivalRateWithSharedState/src/NinjaSteps.cs" />
- <p>At this point, you should be able to run the tests again and not notice any difference in the old functionality.
+ <p>At this point, you should be able to run the tests again and not notice any difference from the old functionality.
Although the old step definition class did not have a constructor and this one does, and it requires a context parameter,
- Cuke4Nuke wires that in automatically using your selected dependency injection container</p>
+ Cuke4Nuke wires this in automatically using your selected dependency injection container</p>
<p>Now we can add another step definition class with a similar constructor to handle the missing steps:</p>
<code file="dotnet/NinjaSurvivalRateWithSharedState/src/SplitSteps.cs" />
<p>We'll leave it to you for homework to implement the missing function for impact analysis
- in the <code>Ninja</code> class and to change the rest of the <code>NinjaSteps.cs</code> to use the context Ninja, not the private one.
+ in the <code>Ninja</code> class and to change the rest of the <code>NinjaSteps.cs</code> to use the context
+ <code>Ninja</code>, not the private one.
If you want to cheat or admit that you are lazy, look for a folder called <code>NinjaSurvivalRateWithSharedState</code> in
the <url link="http://github.com/davedf/cuke4ninja" title="Cuke4Ninja code repository" />.</p>
</section>
+ <section title="Sharing context in Java" id="sec_sharing_context_java">
+ <p>Cuke4Duke loads step definitions from any classes annotated with Cucumber annotations such as
+ <code>@Given</code>, they do not all have to be in the same file. To see this, run your tests again after adding a new, empty
+ feature file. For example, if you are using Maven, run the following command line:</p>
+ <code>mvn integration-test</code>
+ <p>Cucumber will execute the new feature file and find some of the steps in the old <code>NinjaSkillSteps.java</code> step definition class, but report other steps as still undefined (see <link ref="img_java_split" />).</p>
+ <img src="split-java.png" id="img_java_split" title="Cuke4Duke finds some split ninja steps" />
+ <p>The first step, from <code>NinjaSkillSteps.java</code>, creates an internal Ninja object. Because we want to be able to reuse that
+ object in two different step definition classes, we have to enable another step definition class to somehow access that context.
+ Before you even think about saying &lsquo;static&rsquo; know that Chuck Norris moves static objects. Remember that
+ you had to add PicoContainer or Spring to the classpath? Now you'll finally understand why.
+Cuke4Duke shares context between step definitions using
+dependency injection. We can just create a class to hold the context required for our steps and add an instance of that
+ class as a constructor argument for step definition classes. Cuke4Duke will ensure that both classes get the same context.
+ </p>
+ <p>First, let's define a context class. We just need to share the <code>Ninja</code> object so far, so let's
+create a container for a ninja:</p>
+ <code file="java/NinjaSurvivalRateWithSharedState/src/test/java/ninjasurvivalrate/NinjaContext.java"/>
+ <p>Now let's change the existing <code>NinjaSurvivalSteps.java</code> to expect an instance of this new class in the constructor:</p>
+ <code part="constructor" file="java/NinjaSurvivalRateWithSharedState/src/test/java/ninjasurvivalrate/NinjaSurvivalSteps.java" />
+ <p>At this point, you should be able to run the tests again and not notice any difference from the old functionality. Although
+the old step definition class did not have a constructor and this one does, and it requires a context parameter,
+Cuke4Duke wires this in automatically using your selected dependency injection container</p>
+ <p>Now we can add another step definition class with a similar constructor to handle the missing steps:</p>
+ <code file="java/NinjaSurvivalRateWithSharedState/src/test/java/ninjasurvivalrate/SplitSteps.java" />
+ <p>We'll leave it to you for homework to implement the missing function for impact analysis in the <code>Ninja</code> class and
+ to change the rest of the <code>NinjaSurvivalSteps.java</code> to use the context <code>Ninja</code> instead of the private one. If you want to cheat or admit that you are lazy, look for a folder called <code>NinjaSurvivalRateWithSharedState</code> in the <url link="http://github.com/davedf/cuke4ninja" title="Cuke4Ninja code repository" />.</p>
+ </section>
</section>
</chapter>
View
78 plainbook/intro.xml
@@ -12,7 +12,7 @@
development, specification by example and agile acceptance testing. You can use it to
automate functional validation in a form that is easily readable and understandable to business users,
developers and testers. This helps teams create <i>executable specifications</i>,
- that are a goal for development, acceptance criteria and functional regression checks for future changes.
+ that function as goals for development, acceptance criteria and functional regression checks for future changes.
In this way, Cucumber allows teams to create <i>living documentation</i>, a single authoritative source of
information on system functionality that is always up-to-date.
</p>
@@ -21,7 +21,7 @@
Yes, executable specifications. You can use Cucumber to execute checks based on your
specifications against the software automatically, when you use specification by example. To keep
this document short we won't go into specification by example, how behaviour-driven development
- works or how best to apply agile acceptance testing. There are some nice references in
+ works or how best to apply agile acceptance testing. There are some useful references in
<link ref="app_resources"/>
for further research.
</p>
@@ -35,26 +35,25 @@
<section id="sec_why_cucumber" title="Why use Cucumber?">
<p>
Cucumber is one of the rare tools that try very hard to stay out of your way, to let you do your work
- without making you worry about the tool itself too much. It helps teams automate their specifications
+ without you having to worry about the tool itself much. It helps teams automate their specifications
efficiently in several ways:
</p>
<ul>
<li>It is relatively easy to set up.</li>
<li>It supports multiple report formats, including HTML and PDF.</li>
- <li>It supports writing specifications in about 40 spoken languages, making it easy for teams outside of
- English-speaking territories or those working on internationally targeted software to engage their
+ <li>It supports about 40 languages, making it easy for teams in
+ non-English-speaking territories or those working on internationally targeted software to engage their
business users better.
</li>
<li>It supports different ways of describing executable specifications &mdash; including story-like prose,
- lists and tabular data. You can unleash your inner writer, or more likely an inner accountant.
+ lists and tabular data. You can unleash your inner writer, or more likely inner accountant.
</li>
- <li>It allows scripting, abstraction and component reuse on several levels, allowing both technical and
+ <li>It provides for scripting, abstraction and component reuse on several levels, allowing both technical and
non-technical
- users to efficiently describe specifications and tests.
+ users to describe specifications and tests efficiently.
</li>
- <li>It generates the tricky parts of the code so that you don't have to write most of the boiler-plate
- automation or make mistakes doing it.
+ <li>It generates the boiler-plate parts of the code (often tricky) so that you don't have to write it and possibly make mistakes.
</li>
<li>It integrates nicely with the rest of the development ecosystem. It does not try to impose a version
control system, but works off plain-text files that can be stored in any version control system. For
@@ -64,7 +63,7 @@
can use Cucumber with .NET or JVM languages almost natively.
</li>
<li>It's integrated with all the most popular web testing libraries.</li>
- <li>It allows you to cross reference and index tests using tags, so that you can quickly find all tests related to a function or run a group of related tests (eg quick
+ <li>It allows you to cross-reference and index tests using tags, so that you can quickly find all tests related to a function or run a group of related tests (eg quick
tests, slow tests, integration tests, accounting tests).
</li>
</ul>
@@ -77,11 +76,11 @@
</p>
</section>
<section id="sec_comparison_tools" title="How does Cucumber compare to other tools?">
- <p>Cucumber is like that brave Athenian soldier who centuries ago decided to test the resilience of humans by running from the Marathon field. It is essentially a test runner. It does a pretty good job of executing automated tests and spitting out reports in lots of various formats,
+ <p>Cucumber is like that brave Athenian soldier who centuries ago decided to try out whether he could reach Athens from the Marathon battlefield &ndash; it is essentially a test runner. It does a pretty good job of executing automated tests and spitting out reports in lots of various formats,
including HTML, PDF, JUnit (for integration with continuous build tools) and colour text on the
- screen. On the other hand, it doesn't try to do anything before test
- execution in the process. So there is no Cucumber-IDE or anything
- similar. There are some external programs for that, mostly of beta
+ screen. On the other hand, it doesn't try to do anything in the testing process before or after actual test
+ execution. So there is no Cucumber IDE or anything
+ similar. There are some external programs for such purposes, mostly of beta
quality and in development:</p>
<ul>
<li><url link="http://github.com/henritersteeg/cuke4vs" /> does syntax highlighting
@@ -89,64 +88,69 @@
<li><url link="http://github.com/QuBiT/cucumber-eclipse-plugin" /> is a plugin for Eclipse</li>
<li><url link="http://github.com/cs3b/cucumber_fm/" /> is a website system for
Cucumber file management</li>
- <li><url link="http://relishapp.com/" /> is a web product in development to
- publish Cucumber files as a nice web site</li>
+ <li><url link="http://relishapp.com/" /> is a web product that enables you to publish Cucumber files as a website</li>
<li><url link="http://21croissants.github.com/courgette/" /> another web product in
development to show files as web pages</li>
- <li><url link="https://github.com/asterite/cukecooker/" /> is a web based IDE for Cucumber feature files when working in Ruby</li>
+ <li><url link="https://github.com/asterite/cukecooker/" /> is a web-based IDE for Cucumber feature files that you can use when working in Ruby</li>
</ul>
- <p> Cucumber files (explained later in this chapter) are plain-text which makes it very simple to edit them but does not allow any clever formatting.
-You can put a short description in a file header, but apart from that everything else in the file is related to scenarios and test cases. That means that there is no way to include images, hyperlinks, rich-text descriptions and similar.
+ <p>For Ruby automation, RubyMine by JetBrains has pretty good integration with Cucumber. SpecFlow integrates very nicely into Visual Studio.</p>
+ <p> Cucumber files (explained later in this chapter) are in plain text, which makes it very simple to edit them but does not allow any clever formatting.
+You can put a short description in a file header, but apart from that everything else in the file is related to scenarios and test cases. That means that there is no way to include images, hyperlinks, rich-text descriptions and similar adjuncts.
</p>
- <p>Cucumber's step
- definition (automation layer, integration to domain API, explained in later chapters) model is very simple, which is
+ <p>Cucumber's automation model
+ definition (automation layer, integration to domain API, explained in later chapters
+ is very simple, which is
both a strength
- and a weakness in different contexts. A simple API makes it very easy
+ and a weakness. A simple API makes it very easy
to understand and learn what you can do with Cucumber, so you won't
spend three months getting your head around all the possible ways to
- use tables. On the other hand, it doesn't have the smart domain-object
+ use tables. On the other hand, it doesn't have the smart mapping for domain objects
mapping features that some other tools have, which allow you to expose your domain
objects and services directly for testing and provide lots of
flexibility in how you can manage complex scenarios.
</p>
</section>
- <section id="sec_cucumber_bdd" title="What does Cucumber have to do with BDD">
- <p>Aspiring Cucumber ninjas are well advised that Cucumber is frequently mentioned in the context of Behaviour driven Development (BDD). In fact, Cucumber is as closely tied to BDD as Chuck Norris is to his beard. Dan North, the central authority on BDD, summarised BDD as:<footnote>
+ <section title="What does Cucumber have to do with BDD?">
+ <p>Aspiring Cucumber ninjas are well advised that Cucumber is frequently mentioned
+in the context of behaviour-driven development (BDD). In fact, Cucumber is as closely tied to BDD as Chuck Norris is to his beard. Dan North, the central authority on BDD, summarised BDD as:<footnote>
See
<url link="http://skillsmatter.com/podcast/java-jee/how-to-sell-bdd-to-the-business" />
</footnote>
</p>
- <quote>A second generation, outside-in, pull based, multiple stakeholder,
- multiple scale, high automation, agile methodology
+ <quote>A second-generation, outside-in, pull-based, multiple-stakeholder,
+ multiple-scale, high-automation, agile methodology
</quote>
- <section title="Second Generation">
- <p>BDD came out of merging ideas of Extreme Programming, Lean software development and Domain Driven Design,
- as a second-generation agile process that helps teams deliver high quality software and answers many of the
- most confusing questions of early agile processes, such as the ones dealing with documentation and testing.
+ <section title="Second-generation">
+ <p>BDD developed from a merging of the concepts of extreme programming, lean software development and domain-driven design,
+ as a second-generation agile process that helps teams deliver high-quality software and answers many of the
+ most confusing questions of early agile processes, such as those dealing with documentation and testing.
</p>
</section>
<section title="Outside-in and pull-based">
- <p>BDD takes the ideas of value chains and pulling features from Lean manufacturing, ensuring that the right software
+ <p>BDD takes the ideas of value chains and pulling features from lean manufacturing, ensuring that the right software
is built by focusing on the expected outputs of the system and ensuring that these outputs are achieved.
- Teams achieve this by collaborating on specifications and illustrating the specifications with key examples
+ Teams do this by collaborating on specifications and illustrating the specifications with key examples
of expected outcomes. These specifications are created just in time when they are needed, from user stories
and use cases that get pulled into development.</p>
</section>
<section title="Multiple-stakeholder">
<p>BDD dispenses with the idea that there is a single amorphous &lsquo;user&rsquo; of the system, and recognises that
- different groups of people will be affected with the software in different ways. In Quality Software Management, Gerald Weinberg wrote that &lsquo;Quality is value to some person&rsquo;.
+ different groups of people will be involved with the software in different ways. In <i>Quality Software Management</i><bib ref="weinberg:qualitysw"/>, Gerald Weinberg wrote: &lsquo;Quality is value to some person&rsquo;.
BDD forces the delivery teams
to understand and define quality, by specifying <i>who</i> are the people that the software will bring some value to and <i>what</i> will be valuable to them. </p>
</section>
<section title="Multiple-scale">
- <p>A core idea of BDD is to clearly specify the expected outputs with realistic examples and then develop the software to achieve those outputs. This works on multiple levels. On the top level, we can work with high-level examples of how a feature will be useful in a holistic way. Below that, we can look at the impact of that on individual functional areas of the system. Below that we can work on technical implementation units. The process is the same, regardless of the level. </p>
+ <p>A core idea of BDD is to specify the expected outputs clearly with realistic examples and then develop the software to achieve those outputs.
+This works on multiple levels. On the top level, we can work with high-level examples of how a feature will be useful
+ in a holistic way. Below that level, we can look at the impact
+ on individual functional areas of the system. Below that we can work on technical implementation units. The process is the same, regardless of the level. </p>
</section>
<section title="Agile">
<p>BDD works best with short iterations or flow-based work, where teams specify, implement and deliver relatively small
chunks of functionality. It requires cross-functional teams to collaborate on specifications and tests.</p>
</section>
<section title="High-automation">
- <p>Once the expected quality is defined, the team will need to check the system functionality frequently and compare it to the expected results. To make this check efficient, it has to be automated. BDD relies heavily on automation of executable
+ <p>Once the expected quality is defined, the team will need to check the system's functionality frequently and compare it to the expected results. To make this check efficient, it has to be automated. BDD relies heavily on the automation of executable
specifications. </p>
<p>Cucumber takes care of the &lsquo;high-automation&rsquo; part of the BDD definition. It allows teams to describe features in a business language, with key examples, and automate the validation of those features against system functionality.</p>
</section>
View
168 plainbook/tables.xml
@@ -1,16 +1,10 @@
<!DOCTYPE chapter SYSTEM "../resources/plainbook/plainbook.dtd" >
<chapter id="chp_complex_scenarios" title="Managing complex scenarios">
<p>The BDD Given-When-Then scenario structure is too verbose when a specification
- includes lots of similar cases or when complicated objects come into play. One of the strengths of Cucumber is
- handling complex scenarios and groups of scenarios efficiently. It has several features that help developers
+ includes lots of similar cases or when complicated objects come into play. One of the strengths of Cucumber is its ability to
+ handle complex scenarios and groups of scenarios efficiently. It has several features that help developers
and testers write and manage complex specifications yet still keep them in a form that is self-explanatory.
In this chapter, we explore several such features.</p>
- <ul>
- <li>Scenario outlines allow us to group related scenarios and add examples with no loss of clarity</li>
- <li>Tables allow us to define or validate related attributes as a group and handle lists of objects easily</li>
- <li>Backgrounds allow us to group related pre-conditions for a set of scenarios and manage them in one place</li>
- <li>Hooks allow us to programmatically manage cross-cutting technical activities such as transactions, security or external resources.</li>
- </ul>
<section id="sec_more_complex" title="Complex setups and validations">
<p>
@@ -42,16 +36,17 @@
<code>Given</code>, <code>And</code> or <code>But</code>
(or <code>Then</code> and <code>When</code>, actually). Cucumber will match the step using a regular expression
and execute it. Using <code>And</code> and <code>But</code>
- makes it easier to read the specification.
+ just makes it easier to read the specification.
</p>
<p>This also means that the syntax allows you to chain <code>When</code> steps by using <code>And</code> or
-<code>But</code> steps. Though the Gherkin syntax won't stop you, it will send a secret message to Chuck Norris and he will come to your house
- and burn it to the ground with you in it.<footnote>Legal Note: Chuck reserves the right to sub-contract to the Deadly Viper Assassination Squad if he is busy that day.</footnote> A scenario should have one and only one action.</p>
+<code>But</code> steps. Though the Gherkin syntax won't stop you, it will send a secret message to Chuck Norris and
+he will come to your house
+ and burn it to the ground with you in it.<footnote>Legal note: Chuck reserves the right to sub-contract to the Deadly Viper Assassination Squad if he is busy that day.</footnote> A scenario should have one and only one action.</p>
<important title="But I need to execute several actions">
- <p>If you want to put more than one
+ <p>If you want to include more than one
<code>When</code> step, then you have a complex multi-step action that means something to the business.
- That flow should be captured by your domain model. Talk to the business users, decide
- on the name for that flow and specify it using a single
+ This flow should be captured by your domain model. Talk to the business users, decide
+ on the name for the flow and specify it using a single
step, which invokes the appropriate domain model procedure. Don't use feature files for scripting the flow.
</p>
</important>
@@ -59,9 +54,9 @@
</section>
<section id="sec_groups_of_scenarios" title="Managing groups of related scenarios">
<p> The feature file we used
- in the <link ref="chp_feature_files" /> has 13 lines for two scenarios. To specify a business
+ in <link ref="chp_feature_files" /> has 13 lines for 2 scenarios. To specify a business
rule properly we would most likely have to add many more scenarios, that illustrate what happens when a
- ninja is not experienced enough to engage a samurai, when it becomes experienced enough to engage Chuck Norris,
+ ninja is not experienced enough to engage a samurai, and when it becomes experienced enough to engage Chuck Norris,
not to mention other ninjas and types of opponents. If we had to add four lines for every new scenario,
the file would quickly become too big to be readable.
</p>
@@ -84,25 +79,25 @@ Scenario <b>Outline</b>: third-level ninjas engage samurai
<p>The interesting parts are in bold: </p>
<ul>
<li>Instead of <code>Scenario</code>, this block starts with <code>Scenario Outline</code></li>
- <li>The steps have placeholders enclosed into less-than and greater-than (&lt; and &gt;).</li>
+ <li>The steps have placeholders enclosed inside less-than and greater-than symbols (&lt; and &gt;).</li>
<li>There is a new block after the steps, starting with a line that contains the <code>Examples</code> keyword and a colon.</li>
<li>A table in the normal wiki format (pipes separate cells, one row per line) defines the examples and expected outcomes. The first
row of the table contains placeholder names.</li>
</ul>
<p>This feature file is functionally equivalent to the one we used in the previous chapter. The same step definitions
- will be used to execute it. In fact, try that yourself. Delete the two step definitions we used earlier, or create
+ will be used to execute it. In fact, try it yourself. Delete the two step definitions we used earlier, or create
a new feature file and run it using Cucumber. You'll see the same results, just formatted differently. </p>
- <p>What's nice about this format is that we can easily add more examples. To make the specification stronger, let's
+ <p>What's nice about scenario outlines is that we can easily add more examples. To make the specification stronger, let's
add another example that shows that second level ninjas don't even fight against samurai. We add just one line to the table:
<code>
|second |a samurai |run for his life |
</code>
The result is easier to read and understand than if we added another four-line scenario. With more complex
- scenarios, the result is even better.</p>
+ scenarios, the benefit is greater still.</p>
<p>Run Cucumber again and the test should fail. If you are running Ruby, you should get an error similar to the one in
<link ref="fig.ninja_survival_outlines_ruby_fail" />. If you are running Java, you should get an error similar to the one in <link ref="fig.ninja_survival_outlines_java_fail" />.</p>
<img src="ninja_survival_outlines_ruby_fail.png" id="fig.ninja_survival_outlines_ruby_fail" title="Ruby ninjas engage samurai even when they should not" />
-<p>For a homework assignment, change the Ninja class to make this test pass and re-run Cucumber to confirm that you
+<p>For a homework assignment, change the Ninja class to make this test pass and rerun Cucumber to confirm that you
have fixed it.</p>
<img src="ninja_survival_outlines_java_fail.png" id="fig.ninja_survival_outlines_java_fail" title="Java ninjas engage samurai even when they should not" />
@@ -131,11 +126,12 @@ the scenarios become hard to understand and maintain. For example:
<code file="ruby/NinjaSurvivalRate/features/ninja_survival_rate_tables.feature" part="singletable" />
<section id="sec_ruby_tables" title="Reading tables in Ruby">
- <p>In Ruby, tables are converted automatically into <code>Cucumber::Ast::Table</code> objects. A step definition will receive
- the whole table as an argument. The simplest way to use a table is to invoke the <code>hashes</code> method
+ <p>In Ruby, tables are converted automatically into <code>Cucumber::Ast::Table</code> objects. A step definition will
+receive
+ the whole table as an argument. The simplest way to use a table is to invoke the <code>hashes</code> method,
which converts the entire table into an array of hashes, mapping the header values to cell values. Each element of the array
represents a single data row. We can implement
- the step to handle creating a ninja from several properties by grabbing the first (and only) hash and passing that to the
+ the step to handle creating a ninja from several properties by grabbing the first (and only) hash and passing it to the
constructor:
</p>
<code file="ruby/NinjaSurvivalRate/features/step_definitions/ninja_steps.rb" part="singletable" />
@@ -144,8 +140,8 @@ the scenarios become hard to understand and maintain. For example:
</section>
<section id="sec_java_tables" title="Reading tables in Java">
<p>In Java, tables are converted automatically into <code>cuke4duke.Table</code> objects, which
- mirror the API of the <code>Cucumber::Ast::Table</code> ruby class. A step definition
- will receive the entire table as an argument. The easiest way to process a table is to invoke the
+ mirror the API of the <code>Cucumber::Ast::Table</code> ruby class. A step definition will
+ receive the entire table as an argument. The easiest way to process a table is to invoke the
<code>hashes</code> method to get a list of maps that represents the table. Each element of the list represents
one data row as a <code>Map&lt;String,String&gt;</code>, mapping column headers into cell values. For example, this is how
we can create a ninja using the <code>belt_level</code> property of the first map in the list:</p>
@@ -156,7 +152,7 @@ the scenarios become hard to understand and maintain. For example:
<section id="sec_net_tables" title="Reading tables in .NET">
<p>In .NET, tables are converted automatically into <code>Cuke4Nuke.Framework.Table</code> objects, which
- mirror parts of the API of the <code>Cucumber::Ast::Table</code> Ruby class. The Table API in .NET is not
+ mirror parts of the API of the <code>Cucumber::Ast::Table</code> Ruby class. The table API in .NET is not
as nice and clean as it is in Ruby and Java, but it does the job. A step definition
will receive the entire table as an argument. The easiest way to process a table is to invoke the
<code>Hashes</code> method to get a list of dictionaries that represents the table. Each element of the list represents
@@ -171,40 +167,41 @@ the scenarios become hard to understand and maintain. For example:
<section id="sec_object_lists" title="Working with lists of objects">
<p>We can use tables to efficiently describe specifications that work with lists of objects. Instead of a very lengthy
description that explains individual objects with Given-When-Then, we can just specify batches of objects in a table.
-For example, it is a well known fact that only Chuck Norris can perform the roundhouse-kick without wire-fu and that he is
-always extremely dangerous. We can specify that with the following scenario:
+For example, it is a well-known fact that only Chuck Norris can perform the roundhouse kick without wire fu and that he is
+always extremely dangerous. We can specify this with the following scenario:
</p>
<code file="ruby/NinjaSurvivalRate/features/danger_levels.feature"/>
-<p>In this case we are using tables for inputs and expected outputs. Note that the input table does not really have any headers. Unlike
-most other tools based on tables, Cucumber does not require the first row to be a header if that is not required. In addition, although
+<p>In this case we are using tables for inputs (skills) and expected outputs (techniques and levels). Note that the table of skills
+ does not have any headers.
+Unlike most other tools based on tables, Cucumber does not require the first row to be a header if that is not required. In addition, although
the API to pass tables to validation step definitions is the same, Cucumber does not require us to check every value individually. It
has a helper method to compare expected tabular outputs to actual values.
</p>