-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathjbehave.doc
455 lines (334 loc) · 21.5 KB
/
jbehave.doc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
Thucydides is an open source library designed to make it easier to define, implement and report on automated acceptance criteria. Until now, Thucydides tests have been implemented using JUnit or easyb. However the most recent version of Thucydides, version 0.9.x, now lets you write your acceptance criteria using the popular JBehave framework.
=== JBehave and Thucydides
JBehave is an open source BDD framework originally written by Dan North, the inventor of BDD. It is strongly integrated into the JVM world, and widely used by Java development teams wanting to implement BDD practices in their projects.
In JBehave, you write automate your acceptance criteria by writing test stories and scenarios using the familiar BDD "given-when-then" notation, as shown in the following example:
---------------------------------------------------------------------
Scenario: Searching by keyword and category
Given Sally wants to buy some antique stamps for her son
When she looks for ads in the 'Antiques' category containing 'stamps'
Then she should obtain a list of ads related to 'stamps' from the 'Antiques' category
---------------------------------------------------------------------
Scenarios like this go in `.story` files: a story file is designed to contain all the scenarios (acceptence criteria) of a given user story. A story file can also have a narrative section at the top, which gives some background and context about the story being tested:
------------------
In order to find the items I am interested in faster
As a buyer
I want to be able to list all the ads with a particular keyword in the description or title.
Scenario: Searching by keyword and category
Given Sally wants to buy some antique stamps for her son
When she looks for ads in the 'Antiques' category containing 'stamps'
Then she should obtain a list of ads related to 'stamps' from the 'Antiques' category
Scenario: Searching by keyword and location
Given Sally wants to buy a puppy for her son
When she looks for ads in the Pets & Animals category containing puppy in New South Wales
Then she should obtain a list of Pets & Animals ads containing the word puppy
from advertisers in New South Wales
------------------
You usually implement a JBehave story using classes and methods written in Java, Groovy or Scala. You implement the story steps using annotated methods to represent the steps in the text scenarios, as shown in the following example:
---------------------------------------------------------------------
public class SearchSteps {
@Given("Sally wants to buy a $gift for her son")
public void sally_wants_to_buy_a_gift(String gift) {
// test code
}
@When("When she looks for ads in the $category category containing $keyword in $region")
public void looking_for_an_ad(String category, String keyword, String region){
// more test code
}
}
---------------------------------------------------------------------
=== Working with JBehave and Thucydides
Thucydides and JBehave work well together. Thucydides uses simple conventions to make it easier to get started writing and implementing JBehave stories, and reports on both JBehave and Thucydides steps, which can be seamlessly combined in the same class, or placed in separate classes, depending on your preferences.
To get started, you will need to add the Thucydides JBehave plugin to your project. In Maven, just add the following dependencies to your pom.xml file:
---------------------------------------------------------------------
<dependency>
<groupId>net.thucydides</groupId>
<artifactId>thucydides-core</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>net.thucydides</groupId>
<artifactId>thucydides-jbehave-plugin</artifactId>
<version>0.9.0</version>
</dependency>
---------------------------------------------------------------------
New versions come out regularly, so be sure to check the Maven Central repository (http://search.maven.org) to know the latest version numbers for each dependency.
=== Setting up your project and organizing your directory structure
JBehave is a highly flexible tool. The downside of this is that, out of the box, JBehave requires quite a bit of bootstrap code to get started. Thucydides tries to simplify this process by using a convention-over-configuration approach, which significantly reduces the amount of work needed to get started with your acceptance tests. In fact, you can get away with as little as an empty JUnit test case and a sensibly-organized directory structure for your JBehave stories.
==== The JUnit test runner
The JBehave tests are run via a JUnit runner. This makes it easier to run the tests both from within an IDE or as part of the build process. All you need to do is to extend the ThucydidesJUnitStories, as shown here:
---------------------------------------------------------------------
package net.thucydides.showcase.jbehave;
import net.thucydides.jbehave.ThucydidesJUnitStories;
public class JBehaveTestCase extends ThucydidesJUnitStories {
public JBehaveTestCase() {}
}
---------------------------------------------------------------------
When you run this test, Thucydides will run any JBehave stories that it finds in the default directory location. By convention, it will look for a `stories` folder on your classpath, so `src/test/resources/stories' is a good place to put your story files.
==== Organizing your requirements
Placing all of your JBehave stories in one directory does not scale well; it is generally better to organize them in a directory structure that groups them in some logical way. In addition, if you structure your requirements well, Thucydides will be able to provide much more meaningful reporting on the test results.
By default, Thucydides supports a simple directory-based convention for organizing your requirements. The standard structure uses three levels: capabilities, features and stories. A story is represented by a JBehave .story file so two directory levels underneath the `stories` directory will do the trick. An example of this structure is shown below:
----------------------
+ src
+ test
+ resources
+ stories
+ grow_potatoes [a capability]
+ grow_organic_potatoes [a feature]
- plant_organic_potatoes.story [a story]
- dig_up_organic_potatoes.story [another story]
+ grow_sweet_potatoes [another feature]
...
----------------------
If you prefer another hierarchy, you can use the `thucydides.capability.types` system property to override the default convention. For example. if you prefer to organize your requirements in a hierachy consisting of epics, theme and stories, you could set the `thucydides.capability.types` property to 'epic,theme' (the story level is represented by the .story file).
When you start a project, you will typically have a good idea of the capabilities you intent to implement, and probably some of the main features. If you simply store your .story files in the right directory structure, the Thucydides reports will reflect these requirements, even if no tests have yet been specified for them. This is an excellent way to keep track of project progress. At the start of an iteration, the reports will show all of the requirements to be implemented, even those with no tests defined or implemented yet. As the iteration progresses, more and more acceptance criteria will be implemented, until acceptance criteria have been defined and implemented for all of the requirements that need to be developed.
[[jbehave-project-structure]]
.A Thucyides project using JBehave can organize the stories in an appropriate directory structure
image::figs/jbehave-initial-project.png[scaledwidth="80%", width=475]
An optional but useful feature of the JBehave story format is the narrative section that can be placed at the start of a story to help provide some more context about that story and the scenarios it contains. This narrative will appear in the Thucydides reports, to help give product owners, testers and other team members more information about the background and motivations behind each story. For example, if you are working on an online classifieds website, you might want users to be able to search ads using keywords. You could describe this functionality with a textual description like this one:
---------------------
Story: Search for ads by keyword
In order to find the items I am interested in faster
As a buyer
I want to be able to list all the ads with a particular keyword
in the description or title.
---------------------
However to make the reports more useful still, it is a good idea to document not only the stories, but to also do the same for your higher level requirements. In Thucydides, you can do this by placing a text file called `narrative.txt` in each of the requirements directories you want to document (see below). These files follow the JBehave/Cucumber convention for writing narratives, with an optional title on the first line, followed by a narrative section started by the keyword `Narrative:`. For example, for a search feature for an online classifieds web site, you might have a description along the following lines:
---------------------
Search for online ads
Narrative:
In order to increase sales of advertised articles
As a seller
I want potential buyers to be able to display only the ads for
articles that they might be interested in purchasing.
---------------------
When you run these stories (without having implemented any actual tests), you will get a report containing lots of pending tests, but more interestingly, a list of the requirements that need to be implemented, even if there are no tests or stories associated with them yet. This makes it easier to plan an iteration: you will initially have a set of requirements with only a few tests, but as the iteration moves forward, you will typically see the requirements fill out with pending and passing acceptance criteria as work progresses.
[[jbehave-requirements-report]]
.You can see the requirements that you need to implement n the requirements report
image::figs/jbehave-requirements-report.png[scaledwidth="80%", width=475]
===== Narrative in asciidoc format
Narratives can be written in http://www.methods.co.nz/asciidoc/[Asciidoc] for richer formatting. Set the +narrative.format+ property to +asciidoc+ to allow Thucydides to parse the narrative in asciidoc format.
For example, the following narrative,
---------------------
Item search
Narrative:
In order to find the items I am interested in faster
As a +buyer+
*I want to be able to list all the ads with a particular keyword in the description or title*.
---------------------
will be rendered on the report as shown below.
[[asciidoc_narrative]]
.Narrative with asciidoc formatting
image::figs/asciidoc_narrative.png[scaledwidth="80%", width=475]
==== Customizing the requirements module
You can also easily extend the Thucydides requirements support so that it fits in to your own system. This is a two-step process. First, you need to write an implementation of the `RequirementsTagProvider` interface.
-------------------
package com.acme.tests
public class MyRequirementsTagProvider implements RequirementsTagProvider {
@Override
public List<Requirement> getRequirements() {
// Return the full list of available requirements from your system
}
@Override
public Optional<Requirement> getParentRequirementOf(TestOutcome testOutcome) {
// Return the requirement, if any, associated with a particular test result
}
@Override
public Set<TestTag> getTagsFor(TestOutcome testOutcome) {
// Return all the requirements, and other tags, associated with a particular test result
}
}
-------------------
Next, create a text file in your `src/main/resources/META-INF/serices` directory called `net.thucydides.core.statistics.service.TagProvider`, and put the fullly qualified name of your RequirementsTagProvider implementation.
==== Story meta-data
You can use the JBehave Meta tag to provide additional information to Thucydides about the test. The @driver annotation lets you specify what WebDriver driver to use, eg.
----------------------
Meta:
@driver htmlunit
Scenario: A scenario that uses selenium
Given I am on the test page
When I enter the first name <firstname>
And I enter the last name <lastname>
Then I should see <firstname> and <lastname> in the names fields
And I should be using HtmlUnit
Examples:
|firstname|lastname|
|Joe | Blow|
|John | Doe |
-----------------
You can also use the @issue annotation to link scenarios with issues, as illustrated here:
-----------------
Meta:
@issue MYPROJ-1, MYPROJ-2
Scenario: A scenario that works
Meta:
@issues MYPROJ-3,MYPROJ-4
@issue MYPROJ-5
Given I have an implemented JBehave scenario
And the scenario works
When I run the scenario
Then I should get a successful result
-----------------
You can also attribute tags to the story as a whole, or to individual scenarios:
------------------
Meta:
@tag capability:a capability
Scenario: A scenario that works
Meta:
@tags domain:a domain, iteration: iteration 1
Given I have an implemented JBehave scenario
And the scenario works
When I run the scenario
Then I should get a successful result
------------------
==== Implementing the tests
If you want your tests to actually do anything, you will also need classes in which you place your JBehave step implementations. If you place these in any package at or below the package of your main JUnit test, JBehave will find them with no extra configuration.
Thucydides makes no distinction between the JBehave-style @Given, @When and @Then annotations, and the Thucydides-style @Step annotations: both will appear in the test reports. However you need to start with the @Given, @When and @Then-annotated methods so that JBehave can find the correct methods to call for your stories. A method annotated with @Given, @When or @Then can call Thucydides @Step methods, or call page objects directly (though the extra level of abstraction provided by the @Step methods tends to make the tests more reusable and maintainable on larger projects).
A typical example is shown below. In this implementation of one of the scenarios we saw above, the high-level steps are defined using methods annotated with the JBehave @Given, @When and @Then annotations. These methods, in turn, use steps that are implemented in the BuyerSteps class, which contains a set of Thucydides @Step methods. The advantage of using this two-leveled approach is that it helps maintain a degree of separation between the definition of what is being done in a test, and how it is being implemented. This tends to make the tests easier to understand and easier to maintain.
----------------------
public class SearchScenarioSteps {
@Steps
BuyerSteps buyer;
@Given("Sally wants to buy a $present for her son")
public void buyingAPresent(String present) {
buyer.opens_home_page();
}
@When("she looks for ads in the $category category containing $keyword in $region")
public void adSearchByCategoryAndKeywordInARegion(String category,String keyword,String region){
buyer.chooses_region(region);
buyer.chooses_category_and_keywords(category, keyword);
buyer.performs_search();
}
@Then("she should obtain a list of $category ads containing the word $keyword from advertisers in $region")
public void resultsForACategoryAndKeywordInARegion(String category,String keyword,String region){
buyer.should_only_see_results_with_titles_containing(keyword);
buyer.should_only_see_results_from_region(region);
buyer.should_only_see_results_in_category(category);
}
}
---------------
The Thucydides steps can be found in the BuyserSteps class. This class in turn uses Page Objects to interact with the actual web application, as illustrated here:
--------------
public class BuyerSteps extends ScenarioSteps {
HomePage homePage;
SearchResultsPage searchResultsPage;
public BuyerSteps(Pages pages) {
super(pages);
homePage = getPages().get(HomePage.class);
searchResultsPage = getPages().get(SearchResultsPage.class);
}
@Step
public void opens_home_page() {
homePage.open();
}
@Step
public void chooses_region(String region) {
homePage.chooseRegion(region);
}
@Step
public void chooses_category_and_keywords(String category, String keywords) {
homePage.chooseCategoryFromDropdown(category);
homePage.enterKeywords(keywords);
}
@Step
public void performs_search() {
homePage.performSearch();
}
@Step
public void should_only_see_results_with_titles_containing(String title) {
searchResultsPage.allTitlesShouldContain(title);
}
...
}
--------------
The Page Objects are similar to those you would find in any Thucydides project, as well as most WebDriver projects. An example is listed below:
--------------
@DefaultUrl("http://www.newsclassifieds.com.au")
public class HomePage extends PageObject {
@CacheLookup
@FindBy(name="adFilter.searchTerm")
WebElement searchTerm;
@CacheLookup
@FindBy(css=".keywords button")
WebElement search;
public HomePage(WebDriver driver) {
super(driver);
}
public void chooseRegion(String region) {
findBy("#location-select .arrow").then().click();
waitFor(500).milliseconds();
findBy("//ul[@class='dropdown-menu']//a[.='" + region + "']").then().click();
}
public void chooseCategoryFromDropdown(String category) {
getDriver().navigate().refresh();
findBy("#category-select").then(".arrow").then().click();
findBy("//span[@id='category-select']//a[contains(.,'" + category + "')]").then().click();
}
public void enterKeywords(String keywords) {
element(searchTerm).type(keywords);
}
public void performSearch() {
element(search).click();
}
}
--------------
When these tests are executed, the JBehave steps combine with the Thucydides steps to create a narrative report of the test results:
[[thucydides-test-report]]
.You can see the requirements that you need to implement in the requirements report
image::figs/thucydides-test-report.png[scaledwidth="80%", width=475]
=== JBehave Maven Archetype
A jBehave archetype is availble to help you jumpstart a new project. As usual, you can run mvn archetype:generate from the command line and then select the net.thucydides.thucydides-jbehave-archetype archetype from the proposed list of archetypes. Or you can use your favorite IDE to generate a new Maven project using an archetype.
This archetype creates a project directory structure similar to the one shown here:
--------------
+ main
+ java
+ SampleJBehave
+ pages
- DictionaryPage.java
+ steps
- EndUserSteps.java
+ test
+ java
+ SampleJBehave
+ jbehave
- AcceptanceTestSuite.java
- DefinitionSteps.java
+ resources
+ SampleJBehave
+ stories
+ consult_dictionary
- LookupADefinition.story
--------------
=== Running all tests in a single browser window
All web tests can be run in a single browser window using either by setting the thucydides.use.unique.browser system property or programmatically using runThucydides().inASingleSession() inside the junit runner.
---------------------------------------------------------------------
package net.thucydides.showcase.jbehave;
import net.thucydides.jbehave.ThucydidesJUnitStories;
public class JBehaveTestCase extends ThucydidesJUnitStories {
public JBehaveTestCase() {
runThucydides().inASingleSession();
}
}
---------------------------------------------------------------------
== Implementing Step Libraries
Once you have defined the steps you need to describe your high level tests, you need to implement these steps. In an automated web test, test steps represent the level of abstraction between your Page Objects (which are designed in terms of actions that you perform on a given page) and higher-level stories (sequences of more business-focused actions that illustrate how a given user story has been implemented). Steps can contain other steps, and are included in the Thucydides reports. Whenever a step is executed, a screenshot is stored and displayed in the report.
=== Creating Step Libraries
Test steps are regular java methods, annotated with the +@Step+ annotation. You organize steps and step groups in step libraries. A step library is just a normal Java class. If you are running web tests, your step library should either have a +Pages+ member variable, or (more simply) extend the +ScenarioSteps+ class, e.g.
-------------------
public class JobSeekerSteps extends ScenarioSteps {
public JobSeekerSteps(Pages pages) {
super(pages);
}
@Step
public void opens_jobs_page() {
FindAJobPage page = getPages().get(FindAJobPage.class);
page.open();
}
@Step
public void searches_for_jobs_using(String keywords) {
FindAJobPage page = getPages().get(FindAJobPage.class);
page.look_for_jobs_with_keywords(keywords);
}
}
-------------------
Note that step methods can take parameters. The parameters that are passed into a step method will be recorded and reported in the Thucydides reports, making this an excellent technique to make your tests more maintainable and more modular.
Steps can also call other steps, which is very useful for more complicated test scenarios. The result is the sort of nested structure you can see in <<fig-test-report>>.