2. Agenda
The application under test
What is BDD
What is Cucumber
How Cucumber integrates with:
◦ Protractor API
◦ Page object pattern
◦ SerenityJS Framework / Screenplay
pattern
SerenityJS architecture
Run the tests and view the reports
4. Behavior Driven Development
(BDD)
Behavior Driven Development is
the process of exploring,
discovering, defining and driving
the desired behavior of a software
system by using conversation,
concrete examples and
automated tests
6. Cucumber
It is a language, a process, and a tool
Provides a single source of truth of
software behavior
Appeals to both non-technical and
technical project members
Combines into one format
◦ automated acceptance tests
◦ functional requirements
◦ software documentation
7. Feature: Browse the speakers
As Harry (a conference visitor)
I want to browse the Speakers or search for some
of them by their name.
I would like also to read more about a specific
speaker, his interests, his skills etc.
@issues:JIRA-1,JIRA-2
Scenario: Access to the Speakers page
Given that Harry visits the conference page
When he navigates to menu called 'Speakers’
Then he sees a page titled 'Speakers’
Scenario: Searching for a speaker
Given that Harry visits the conference page
When he searches for speakers named 'Jason’
Then he sees only the speakers with the name
| Jason Poon |
| Jason Lengstorf |
Scenario
9. Feature: Browse the speakers
As Harry (a conference visitor) I want to browse
the Speakers
Scenario: Access to the Speakers page
Given that Harry visits the conference page
When he navigates to menu called 'Speakers’
Then he sees a page titled 'Speakers’
When he navigates to menu called 'Speakers’
Step implementation
A
scenario A step
@when(/^s?he navigates to menu called '(.*)'$/)
public navigateToMenuCalled(name: string) {
return element(by.css('.navbar-nav’))
element(by.linkText(name)).click();
The step
definition
return element(by.css('.navbar-nav’))
.element(by.linkText(name)).click();
ProtractorAPI
11. Page Object (example)
export class SpeakersPageImpl {
private rootElement = element(
by.css('app-speakers'));
private title = this.rootElement.element(
by.css('.speakers-title'));
private searchBox = this.rootElement.element(
by.css('.speakers-searchbox'));
private speakersGrid: GridWidget = new
GridWidgetImpl(this.rootElement);
12. Page Object (example)
public getTitle(): Promise<string> {
return this.title.getText();
}
public getResultsCount(): Promise<number> {
return this.speakersGrid.getResultsCount();
}
public searchSpeaker(name: string): Promise<void> {
this.searchBox.clear();
return this.searchBox.sendKeys(name.trim());
}
}
13. Feature: Browse the speakers
As Harry (a conference visitor) I want to browse
the Speakers
Scenario: Access to the Speakers page
Given that Harry visits the conference page
When he navigates to menu called 'Speakers’
Then he sees a page titled 'Speakers’
When he navigates to menu called 'Speakers’
The Page Object pattern
A
scenario A step
@when(/^s?he navigates to menu called '(.*)'$/)
public navigateToMenuCalled(name: string) {
return this.homePagePO.clickOnMenu(name);
}
return this.homePagePO.clickOnMenu(name);
export class HomePageImpl implements HomePage {
private mainMenu = element(by.css('.navbar-nav'));
public clickOnMenu(name: string): Promise<void> {
return this.mainMenu.element(by.linkText(
name)).click();
}
The step
definition
return this.mainMenu.element(by.linkText(
name)).click();PO
Implementation
Pageobjectpattern
15. Page Objects – CONs
They easily become bloated and
cumbersome
They often violate the Single
Responsibility Principle
They may need modifications to
extend, in order to be reused to
slightly different pages
Read more:
https://dzone.com/articles/page-objects-refactored-solid-steps-to-the-screenp
16. Serenity/JS BDD
Layered approach with the Screenplay
Pattern
Living documentation with narrative
reports
Follows the SOLID design principles
Integration with Cucumber, Mocha and
Chai
18. The Screenplay Pattern
User-centered model
Small reusable interaction
components
Tests written in a narrative
style
Obeys the SOLID principles
Tests are easily scalable and
maintainable
19. The Screenplay Pattern
The Scenario has Roles and a Goal
The Role is played by Actors
The Actor
◦ has Abilities
◦ performs Tasks
◦ interacts with the system through
Actions
◦ asks Questions about the State of
Element on the Screen
21. Feature: Browse the speakers
As Harry (a conference visitor) I want to browse
the Speakers
Scenario: Access to the Speakers page
Given that Harry visits the conference page
When he navigates to menu called 'Speakers’
Then he sees a page titled 'Speakers’
When he navigates to menu called 'Speakers’
The Serenity/JS layered
architecture
A goal
A task
@when(/^s?he navigates to menu called '(.*)'$/)
public navigateToMenuCalled(name: string) {
return this.stage.theActorInTheSpotlight()
.attemptsTo(
NavigateToMenu.called(name)
);
}
NavigateToMenu.called(name)
The task
definition
export class NavigateToMenu implements Task {
...
@step('{0} navigates to menu called "#name"')
performAs(actor: PerformsTasks) {
return actor.attemptsTo(
Click.on(MainMenu.menuItem(this.name)));
}}
An interaction Click.on(MainMenu.menuItem(this.name)));
Screenplaypattern
The actor
Behavior driven development είναι μια πειθαρχημένη διαδικασία ανάπτυξης λογισμικού κατά την οποία συνεργάζονται ο ιδιοκτήτης του προϊόντος, ο αναλυτής και ο τεστερ
για να προσδιορίσουν τη συμπεριφορά του λογισμικού μέσα από σαφή παραδείγματα και αυτοματοποιημένα τεστ
Η συμπεριφορά του λογισμικού καταγράφεται υπο τη μορφή κριτηρίων αποδοχής (acceptance criteria) και αποτελεί ταυτόχρονα και τεκμηρίωση για το παραγόμενο προϊόν.
Behavior -> Conversation -> User stories -> Written down in DSL (text based language) -> Scenarios ( = Examples) -> Automated tests
Behavior-driven development specifies that tests of any unit of software should be specified in terms of the desired behavior of the unit.[2][4][11] Borrowing from agile software development the "desired behavior" in this case consists of the requirements set by the business — that is, the desired behavior that has business value for whatever entity commissioned the software unit under construction.[2][11] Within BDD practice, this is referred to as BDD being an "outside-in" activity.[12]
Behavioral specifications
Following this fundamental choice, a second choice made by BDD relates to how the desired behavior should be specified. In this area BDD chooses to use a semi-formal format for behavioral specification which is borrowed from user story specifications from the field of object-oriented analysis and design. The scenario aspect of this format may be regarded as an application of Hoare logic to behavioral specification of software units using the Domain Language of the situation.
BDD specifies that business analysts and developers should collaborate in this area and should specify behavior in terms of user stories, which are each explicitly written down in a dedicated document.[11][12] Each user story should, in some way, follow the following structure:[2][12]
S.R.P for short - this principle states that:
A class should have one and only one reason to change, meaning that a class should have only one job.
Objects or entities should be open for extension, but closed for modification.
Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.
A client should never be forced to implement an interface that it doesn't use or clients shouldn't be forced to depend on methods they do not use.
Entities must depend on abstractions not on concretions. It states that the high level module must not depend on the low level module, but they should depend on abstractions.
Many teams use the Page Object pattern to reduce duplication and make maintenance easier. Page Objects raise the level of abstraction from fields and buttons, to screens and web pages. A Page Object hides the details of how to locate an element behind a friendlier-looking API.
DRY - Don’t Repeat Yourself. Avoid repeating the same information in more than one place - as this makes it easier to update when it changes. For example, many of your tests will start by logging in to the application. If we just copy/pasted the first example into every test, then if the CSS selector for the username textbox changes, we’d have to change it in every test. If instead every test accesses it via the LoginPage class, we only have to change the selector in a single place. It’s usually best to assume that everything will change eventually.
Abstraction. We have moved from describing CSS selectors and keyboard/mouse actions to describing intent. The top example is full of details we need to consider together to work out what the test is trying to do, whereas in the second the details are ‘abstracted away’, replaced with an effortless statement of what we want to achieve. This also helps us fix code when it’s broken: the method name tells us what the intent of the code is - so if it does something else we know it’s broken.
Encapsulation. By making the selectors into private fields in the PageObject class, we hide them from external view and ensure they’re only accessible where they’re needed. This simplifies the public interface of the class and makes it easier to use it correctly. If they were publicly available then the next person might (reasonably) expect it was ok to refer to them directly from their test. In general your life will be easier if a class exposes either information or operations - but not both.
Over-encapsulation. Encapsulation (and software design in general) can be great for reducing the cost of anticipated changes but often increases the cost of unanticipated changes. For example, if we want to add new test functionality that re-uses the username and password selectors, we can’t do it without first modifying the PageObject, as the selectors aren’t ‘visible’ outside of it. This makes it harder to try things out quickly and increases the costs of adding new tests.
Re-use between pages. Test code for functionality that occurs on many pages (e.g. a search box), shouldn’t need to be duplicated on every PageObject that needs it. Mix-ins and traits can be used to great effect, but in languages that don’t support them single-inheritance creates rigid hierarchies which cause more problems than they solve. If PageX needs test functionality A, B and C, what order do they all have to inherit each other - and if it loses functionality C later, how does the order have to change? Time spent debating these questions is time not spent adding tests.
Separation of concerns. We separated our intent from our implementation, which is a great start - but our PageObject class still contains information about how to find elements on a page, what actions we can perform on them, and what business tasks we’re trying to accomplish with them.
Proposed by Antony Marcano 2007
https://serenitydojo.teachable.com/blog/1107150/user-centric-and-task-driven-automation
The primary role of automated acceptance tests is to illustrate how a user interacts with the system. They should give us more confidence that the application does all that we expect of it. This role as feedback and living documentation tools is critical; if you can’t tell what a test does in functional terms, how can you have confidence when it passes? And how can you know what to do with the test if it fails?
Too often, our acceptance tests end up as sequences of “click”s and “select”s running against a web application. This makes our tests hard to understand and hard to maintain. User-centric, task-driven test automation shows us a better way.
Nouns and Verbs
Another way of thinking about user-centric test design is in terms of what we model. When we build an application, we reason in terms of domain objects. We might reason in terms of Customers, Destinations, and Travel Insurance Policies. And when we implement the application, we might think about the screens and fields that will represent these concepts.
Proposed by Antony Marcano 2007
https://serenitydojo.teachable.com/blog/1107150/user-centric-and-task-driven-automation
The primary role of automated acceptance tests is to illustrate how a user interacts with the system. They should give us more confidence that the application does all that we expect of it. This role as feedback and living documentation tools is critical; if you can’t tell what a test does in functional terms, how can you have confidence when it passes? And how can you know what to do with the test if it fails?
Too often, our acceptance tests end up as sequences of “click”s and “select”s running against a web application. This makes our tests hard to understand and hard to maintain. User-centric, task-driven test automation shows us a better way.
Nouns and Verbs
Another way of thinking about user-centric test design is in terms of what we model. When we build an application, we reason in terms of domain objects. We might reason in terms of Customers, Destinations, and Travel Insurance Policies. And when we implement the application, we might think about the screens and fields that will represent these concepts.