SlideShare a Scribd company logo
1 of 192
Download to read offline
serenity-bdd.info#SerenityBDD @Wakaleo
Sustainable Test Automation
with Serenity BDD and Screenplay
serenity-bdd.info#SerenityBDD @Wakaleo
John Ferguson Smart
Author of ‘BDD in Action’

Lead Developer of Serenity BDD
Founder of the Serenity Dojo
serenity-bdd.info#SerenityBDD @Wakaleo
serenity-bdd.info#SerenityBDD @Wakaleo
serenity-bdd.info#SerenityBDD @Wakaleo
Want to follow along?
bit.ly/serentiy-planner
serenity-bdd.info#SerenityBDD @Wakaleo
We ❤ E2E
serenity-bdd.info#SerenityBDD @Wakaleo
We 💔 E2E
(said no one ever)
serenity-bdd.info#SerenityBDD @Wakaleo
Execution time: minutes → hours
serenity-bdd.info#SerenityBDD @Wakaleo
Execution time: minutes → hours
Writing: hours → days
serenity-bdd.info#SerenityBDD @Wakaleo
Execution time: minutes → hours
Writing: hours → days
Analysis and reporting: hours → days
serenity-bdd.info#SerenityBDD @Wakaleo
Execution time: minutes → hours
Writing: hours → days
Analysis and reporting: hours → days
Maintenance: days → weeks
serenity-bdd.info#SerenityBDD @Wakaleo
The difference between

scalable tests 

and a world of pain

is design.
serenity-bdd.info#SerenityBDD @Wakaleo
Testing the Journey Planner
#SerenityBDD @Wakaleo serenity-bdd.info
#SerenityBDD @Wakaleo serenity-bdd.info
When a commuter wants to travel 

from Waterloo to Canary Wharf 

around 9:00 AM they should be told 

about the 8:59 AM Jubilee Line train.
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
Enter “Waterloo”
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
Enter “Waterloo”
Pick the first suggestion
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
Enter “Waterloo”
Pick the first suggestion
Wait for the suggestions
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
Enter “Waterloo”
Pick the first suggestion
Wait for the suggestions
Press ▼
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
Enter “Waterloo”
Pick the first suggestion
Wait for the suggestions
Press ▼

Press ↩
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
Enter “Waterloo”
Pick the first suggestion
Wait for the suggestions
Press ▼

Press ↩
#SerenityBDD @Wakaleo serenity-bdd.info
Open the Journey Planner

Choose origin: Waterloo
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Click on the “change time” link
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Click on the “change time” link
Click on the “Leaving” button
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Click on the “change time” link
Click on the “Leaving” button
Select “09:00”
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Confirm selection
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Confirm selection
Click on the “Plan my journey”
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Confirm selection
Click on the “Plan my journey”
Wait for the results to load
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Confirm selection
serenity-bdd.info#SerenityBDD @Wakaleo
Open the Journey Planner

Choose origin: Waterloo
Choose destination: Canary Wharf
Choose time of departure: 9:00 AM
Confirm selection
See a Jubilee Line train at 08:59
serenity-bdd.info#SerenityBDD @Wakaleo
At a high level,
it’s just 6 tasks
serenity-bdd.info#SerenityBDD @Wakaleo
Scripting the flow
serenity-bdd.info#SerenityBDD @Wakaleo
private final By origin = By.id("InputFrom");
private final By originSuggestions = By.cssSelector(".tt-suggestions");
private final By destination = By.id("InputTo");
private final By destinationSuggestions = By.cssSelector(".tt-suggestions");
private final By changeTimeLink = By.partialLinkText("change time");
private final By leavingButton = By.cssSelector("label[for=departing]");
private final By timeSelector = By.id("Time");
private final By planMyJourneyButton = By.cssSelector("#plan-a-journey .plan-journey-button");
private final By journeyResults = By.cssSelector(".summary-results");
private final By fastestJourney = By.cssSelector(".publictransport-box span.time");
serenity-bdd.info#SerenityBDD @Wakaleo
browser.get("https://tfl.gov.uk/");
serenity-bdd.info#SerenityBDD @Wakaleo
browser.get(“https://tfl.gov.uk/");
browser.findElement(origin).sendKeys("Waterloo");
serenity-bdd.info#SerenityBDD @Wakaleo
browser.get(“https://tfl.gov.uk/");
browser.findElement(origin).sendKeys(“Waterloo");
WebDriverWait wait = new WebDriverWait(browser, 10);
wait.until(visibilityOfElementLocated(originSuggestions));
serenity-bdd.info#SerenityBDD @Wakaleo
browser.get(“https://tfl.gov.uk/");
browser.findElement(origin).sendKeys(“Waterloo");
WebDriverWait wait = new WebDriverWait(browser, 10);
wait.until(visibilityOfElementLocated(originSuggestions));
browser.findElement(originSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(originSuggestions).sendKeys(ENTER);
serenity-bdd.info#SerenityBDD @Wakaleo
browser.get(“https://tfl.gov.uk/");
browser.findElement(origin).sendKeys(“Waterloo");
WebDriverWait wait = new WebDriverWait(browser, 10);
wait.until(visibilityOfElementLocated(originSuggestions));
browser.findElement(originSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(originSuggestions).sendKeys(ENTER);
browser.findElement(destination).sendKeys("Canary Wharf");
serenity-bdd.info#SerenityBDD @Wakaleo
browser.get(“https://tfl.gov.uk/");
browser.findElement(origin).sendKeys(“Waterloo");
WebDriverWait wait = new WebDriverWait(browser, 10);
wait.until(visibilityOfElementLocated(originSuggestions));
browser.findElement(originSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(originSuggestions).sendKeys(ENTER);
browser.findElement(destination).sendKeys("Canary Wharf”);
wait.until(visibilityOfElementLocated(destinationSuggestions));
browser.findElement(destinationSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(destinationSuggestions).sendKeys(ENTER);
serenity-bdd.info#SerenityBDD @Wakaleo
…
browser.findElement(changeTimeLink).click();
browser.findElement(leavingButton).click();
new Select(browser.findElement(timeSelector))
.selectByVisibleText("09:00");
serenity-bdd.info#SerenityBDD @Wakaleo
…
browser.findElement(changeTimeLink).click();
browser.findElement(leavingButton).click();
new Select(browser.findElement(timeSelector))
.selectByVisibleText(“09:00");
browser.findElement(planMyJourneyButton).click();
wait.until(visibilityOfElementLocated(journeyResults));
serenity-bdd.info#SerenityBDD @Wakaleo
…
String fastestDepartureTime =
startTimeOf(browser.findElement(fastestJourney).getText());
assertThat(fastestDepartureTime).isEqualTo("08:59");
serenity-bdd.info#SerenityBDD @Wakaleo
private final By origin = By.id("InputFrom");
private final By originSuggestions = By.cssSelector(".tt-suggestions");
private final By destination = By.id("InputTo");
private final By destinationSuggestions = By.cssSelector(".tt-suggestions");
private final By changeTimeLink = By.partialLinkText("change time");
private final By leavingButton = By.cssSelector("label[for=departing]");
private final By timeSelector = By.id("Time");
private final By planMyJourneyButton = By.cssSelector("#plan-a-journey .plan-journey-button");
private final By journeyResults = By.cssSelector(".summary-results");
private final By fastestJourney = By.cssSelector(".publictransport-box span.time");
private WebDriver browser;
@Before
public void openBrowser() {
browser = new FirefoxDriver();
}
@After
public void closeBrowser() {
browser.quit();
}
@Test
public void planning_a_journey() {
browser.get("https://tfl.gov.uk/");
browser.findElement(origin).sendKeys("Waterloo");
WebDriverWait wait = new WebDriverWait(browser, 10);
wait.until(visibilityOfElementLocated(originSuggestions));
browser.findElement(originSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(originSuggestions).sendKeys(ENTER);
browser.findElement(destination).sendKeys("Canary Wharf");
wait.until(visibilityOfElementLocated(destinationSuggestions));
browser.findElement(destinationSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(destinationSuggestions).sendKeys(ENTER);
browser.findElement(changeTimeLink).click();
browser.findElement(leavingButton).click();
new Select(browser.findElement(timeSelector)).selectByVisibleText("09:00");
browser.findElement(planMyJourneyButton).click();
wait.until(visibilityOfElementLocated(journeyResults));
String fastestDepartureTime = startTimeOf(browser.findElement(fastestJourney).getText());
6 tasks ?
serenity-bdd.info#SerenityBDD @Wakaleo
6 tasks buried in
36 lines of noise
private final By origin = By.id("InputFrom");
private final By originSuggestions = By.cssSelector(".tt-suggestions");
private final By destination = By.id("InputTo");
private final By destinationSuggestions = By.cssSelector(".tt-suggestions");
private final By changeTimeLink = By.partialLinkText("change time");
private final By leavingButton = By.cssSelector("label[for=departing]");
private final By timeSelector = By.id("Time");
private final By planMyJourneyButton = By.cssSelector("#plan-a-journey .plan-journey-button");
private final By journeyResults = By.cssSelector(".summary-results");
private final By fastestJourney = By.cssSelector(".publictransport-box span.time");
private WebDriver browser;
@Before
public void openBrowser() {
browser = new FirefoxDriver();
}
@After
public void closeBrowser() {
browser.quit();
}
@Test
public void planning_a_journey() {
browser.get("https://tfl.gov.uk/");
browser.findElement(origin).sendKeys("Waterloo");
WebDriverWait wait = new WebDriverWait(browser, 10);
wait.until(visibilityOfElementLocated(originSuggestions));
browser.findElement(originSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(originSuggestions).sendKeys(ENTER);
browser.findElement(destination).sendKeys("Canary Wharf");
wait.until(visibilityOfElementLocated(destinationSuggestions));
browser.findElement(destinationSuggestions).sendKeys(ARROW_DOWN);
browser.findElement(destinationSuggestions).sendKeys(ENTER);
browser.findElement(changeTimeLink).click();
browser.findElement(leavingButton).click();
new Select(browser.findElement(timeSelector)).selectByVisibleText("09:00");
browser.findElement(planMyJourneyButton).click();
wait.until(visibilityOfElementLocated(journeyResults));
String fastestDepartureTime = startTimeOf(browser.findElement(fastestJourney).getText());
#SerenityBDD @Wakaleo
“
serenity-bdd.info
If you have WebDriver APIs
in your test methods,
You’re Doing It Wrong!
- Simon Stewart, creator of WebDriver
serenity-bdd.info#SerenityBDD @Wakaleo
A test system needs design
serenity-bdd.info#SerenityBDD @Wakaleo
Page Objects to the rescue?
serenity-bdd.info#SerenityBDD @Wakaleo
serenity-bdd.info#SerenityBDD @Wakaleo
@DefaultUrl(“https://tfl.gov.uk")
public class JourneyPlannerPage extends PageObject {
// Locators
// Actions
}
serenity-bdd.info#SerenityBDD @Wakaleo
@DefaultUrl(“https://tfl.gov.uk")
public class JourneyPlannerPage extends PageObject {
// Locators
@FindBy(id = "InputFrom")
private WebElementFacade origin;
@FindBy(className = "tt-suggestions")
private WebElementFacade originSuggestions;
@FindBy(id = “InputTo")
private WebElementFacade destination;
@FindBy(className = “tt-suggestions")
private WebElementFacade destinationSuggestions;
@FindBy(partialLinkText = "change time”)
private WebElementFacade changeTimeLink;
…
serenity-bdd.info#SerenityBDD @Wakaleo
@DefaultUrl(“https://tfl.gov.uk")
public class JourneyPlannerPage extends PageObject {
// Locators
…
// Actions
public void chooseOriginOf(String originValue) {
origin.sendKeys(originValue);
waitFor(visibilityOf(originSuggestions));
originSuggestions.sendKeys(Keys.ARROW_DOWN);
originSuggestions.sendKeys(Keys.ENTER);
}
public void chooseDestinationOf(String destinationValue) {…}

public void confirmSelection() {…}
}
serenity-bdd.info#SerenityBDD @Wakaleo
@RunWith(SerenityRunner.class)
public class JourneyPlannerWithPageObjectsTest {


serenity-bdd.info#SerenityBDD @Wakaleo
@RunWith(SerenityRunner.class)
public class JourneyPlannerWithPageObjectsTest {
@Managed WebDriver driver;


serenity-bdd.info#SerenityBDD @Wakaleo
@RunWith(SerenityRunner.class)
public class JourneyPlannerWithPageObjectsTest {
@Managed WebDriver driver;
JourneyPlannerPage journeyPlanner;
JourneyResultsPage journeyResults;


serenity-bdd.info#SerenityBDD @Wakaleo
JourneyPlannerPage journeyPlanner;
JourneyResultsPage journeyResults;
journeyPlanner.open();
journeyPlanner.chooseOriginOf(“Waterloo");
journeyPlanner.chooseDestinationOf("Canary Wharf”);
journeyPlanner.chooseTimeOfDeparture(“09:00”);
journeyPlanner.confirmSelection();


serenity-bdd.info#SerenityBDD @Wakaleo
JourneyPlannerPage journeyPlanner;
JourneyResultsPage journeyResults;
journeyPlanner.open();
journeyPlanner.chooseOriginOf(“Waterloo");
journeyPlanner.chooseDestinationOf("Canary Wharf”);
journeyPlanner.chooseTimeOfDeparture(“09:00”);
journeyPlanner.confirmSelection();
assertThat(journeyResults.getFastestJourneyDepartureTime())
.isEqualTo(“08:59");
serenity-bdd.info#SerenityBDD @Wakaleo
@RunWith(SerenityRunner.class)
public class JourneyPlannerWithPageObjectsTest {
@Managed
WebDriver driver;
JourneyPlannerPage journeyPlanner;
JourneyResultsPage journeyResults;
@Test
public void planning_a_journey() {
journeyPlanner.open();
journeyPlanner.chooseOriginOf("Waterloo");
journeyPlanner.chooseDestinationOf("Canary Wharf");
journeyPlanner.chooseTimeOfDeparture("09:00");
journeyPlanner.confirmSelection();
assertThat(journeyResults.getFastestJourneyDepartureTime())
.isEqualTo("08:59");
}
}
not bad!
serenity-bdd.info#SerenityBDD @Wakaleo
not bad?
@RunWith(SerenityRunner.class)
public class JourneyPlannerWithPageObjectsTest {
@Managed
WebDriver driver;
JourneyPlannerPage journeyPlanner;
JourneyResultsPage journeyResults;
@Test
public void planning_a_journey() {
journeyPlanner.open();
journeyPlanner.chooseOriginOf("Waterloo");
journeyPlanner.chooseDestinationOf("Canary Wharf");
journeyPlanner.chooseTimeOfDeparture("09:00");
journeyPlanner.confirmSelection();
assertThat(journeyResults.getFastestJourneyDepartureTime())
.isEqualTo("08:59");
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
@DefaultUrl("https://tfl.gov.uk")
public class JourneyPlannerPage extends PageObject {
@FindBy(id = "InputFrom")
private WebElementFacade origin;
@FindBy(className = "tt-suggestions")
private WebElementFacade originSuggestions;
@FindBy(id = "InputTo")
private WebElementFacade destination;
@FindBy(className = "tt-suggestions")
private WebElementFacade destinationSuggestions;
@FindBy(partialLinkText = "change time")
private WebElementFacade changeTimeLink;
@FindBy(css = "label[for=departing]")
private WebElementFacade leavingButton;
@FindBy(id = "Time")
private WebElement timeSelector;
@FindBy(css = "#plan-a-journey .plan-journey-button")
private WebElementFacade planMyJourneyButton;
@FindBy(className = "summary-results")
private WebElementFacade journeyResults;
public void chooseOriginOf(String originValue) {
origin.sendKeys(originValue);
waitFor(visibilityOf(originSuggestions));
originSuggestions.sendKeys(Keys.ARROW_DOWN);
originSuggestions.sendKeys(Keys.ENTER);
}
public void chooseDestinationOf(String destinationValue) {
destination.sendKeys(destinationValue);
waitFor(visibilityOf(destinationSuggestions));
destinationSuggestions.sendKeys(Keys.ARROW_DOWN);
destinationSuggestions.sendKeys(Keys.ENTER);
}
public void chooseTimeOfDeparture(String timeOfDeparture) {
changeTimeLink.click();
leavingButton.click();
selectFromDropdown(timeSelector, timeOfDeparture);
}
public void confirmSelection() {
planMyJourneyButton.click();
waitFor(visibilityOf(journeyResults));
}
large classes
serenity-bdd.info#SerenityBDD @Wakaleo
large classes
bloated methods
@DefaultUrl("https://tfl.gov.uk")
public class JourneyPlannerPage extends PageObject {
@FindBy(id = "InputFrom")
private WebElementFacade origin;
@FindBy(className = "tt-suggestions")
private WebElementFacade originSuggestions;
@FindBy(id = "InputTo")
private WebElementFacade destination;
@FindBy(className = "tt-suggestions")
private WebElementFacade destinationSuggestions;
@FindBy(partialLinkText = "change time")
private WebElementFacade changeTimeLink;
@FindBy(css = "label[for=departing]")
private WebElementFacade leavingButton;
@FindBy(id = "Time")
private WebElement timeSelector;
@FindBy(css = "#plan-a-journey .plan-journey-button")
private WebElementFacade planMyJourneyButton;
@FindBy(className = "summary-results")
private WebElementFacade journeyResults;
public void chooseOriginOf(String originValue) {
origin.sendKeys(originValue);
waitFor(visibilityOf(originSuggestions));
originSuggestions.sendKeys(Keys.ARROW_DOWN);
originSuggestions.sendKeys(Keys.ENTER);
}
public void chooseDestinationOf(String destinationValue) {
destination.sendKeys(destinationValue);
waitFor(visibilityOf(destinationSuggestions));
destinationSuggestions.sendKeys(Keys.ARROW_DOWN);
destinationSuggestions.sendKeys(Keys.ENTER);
}
public void chooseTimeOfDeparture(String timeOfDeparture) {
changeTimeLink.click();
leavingButton.click();
selectFromDropdown(timeSelector, timeOfDeparture);
}
public void confirmSelection() {
planMyJourneyButton.click();
waitFor(visibilityOf(journeyResults));
}
serenity-bdd.info#SerenityBDD @Wakaleo
large classes
bloated methods
difficult to compose
@DefaultUrl("https://tfl.gov.uk")
public class JourneyPlannerPage extends PageObject {
@FindBy(id = "InputFrom")
private WebElementFacade origin;
@FindBy(className = "tt-suggestions")
private WebElementFacade originSuggestions;
@FindBy(id = "InputTo")
private WebElementFacade destination;
@FindBy(className = "tt-suggestions")
private WebElementFacade destinationSuggestions;
@FindBy(partialLinkText = "change time")
private WebElementFacade changeTimeLink;
@FindBy(css = "label[for=departing]")
private WebElementFacade leavingButton;
@FindBy(id = "Time")
private WebElement timeSelector;
@FindBy(css = "#plan-a-journey .plan-journey-button")
private WebElementFacade planMyJourneyButton;
@FindBy(className = "summary-results")
private WebElementFacade journeyResults;
public void chooseOriginOf(String originValue) {
origin.sendKeys(originValue);
waitFor(visibilityOf(originSuggestions));
originSuggestions.sendKeys(Keys.ARROW_DOWN);
originSuggestions.sendKeys(Keys.ENTER);
}
public void chooseDestinationOf(String destinationValue) {
destination.sendKeys(destinationValue);
waitFor(visibilityOf(destinationSuggestions));
destinationSuggestions.sendKeys(Keys.ARROW_DOWN);
destinationSuggestions.sendKeys(Keys.ENTER);
}
public void chooseTimeOfDeparture(String timeOfDeparture) {
changeTimeLink.click();
leavingButton.click();
selectFromDropdown(timeSelector, timeOfDeparture);
}
public void confirmSelection() {
planMyJourneyButton.click();
waitFor(visibilityOf(journeyResults));
}
serenity-bdd.info#SerenityBDD @Wakaleo
Four Software Engineering Principles can help
you write more maintainable tests
serenity-bdd.info#SerenityBDD @Wakaleo
1) Separation of Concerns
Hide complexity through layers of abstraction
serenity-bdd.info#SerenityBDD @Wakaleo
2) Single Responsibility Principle
A class should have only one reason to change
serenity-bdd.info#SerenityBDD @Wakaleo
3) Open-Closed Principle
Green diffs
serenity-bdd.info#SerenityBDD @Wakaleo
4) Narrative coding style
Express your intent
serenity-bdd.info#SerenityBDD @Wakaleo
Serenity BDD and
the Screenplay Pattern
#SerenityBDD @Wakaleo
“
serenity-bdd.info
Your test framework should not
model how your system behaves.
It should model how your user
behaves.
serenity-bdd.info#SerenityBDD @Wakaleo
serenity-bdd.info#SerenityBDD @Wakaleo
Goals
What are you trying to achieve?
serenity-bdd.info#SerenityBDD @Wakaleo
Goals
What are you trying to achieve?
Tasks
What do you need to do to
achieve this goal?
serenity-bdd.info#SerenityBDD @Wakaleo
Goals
What are you trying to achieve?
Tasks
What do you need to do to
achieve this goal?
Interactions
What interactions with the
system do you need for each
task?
serenity-bdd.info#SerenityBDD @Wakaleo
Actor
Connor the
commuter
serenity-bdd.info#SerenityBDD @Wakaleo
Plan my journey
Actor Goals
Connor the
commuter
serenity-bdd.info#SerenityBDD @Wakaleo
Plan my journey
Actor Goals Tasks
Choose Destination
Choose Origin
Choose time of departure
Confirm selection
Connor the
commuter
serenity-bdd.info#SerenityBDD @Wakaleo
Plan my journey
Actor Goals Tasks Interactions
Press “Down”
Type “Waterloo”
Choose Destination
Choose Origin
Choose time of departure
Press “Enter”
Confirm selection
Connor the
commuter
serenity-bdd.info#SerenityBDD @Wakaleo
Say what you mean
serenity-bdd.info#SerenityBDD @Wakaleo


Open the Journey Planner

Choose origin of Waterloo
Choose destination of Canary Wharf
Choose time of departure: 09:00
Confirm selection
See if the fastest train departs at 08:59
serenity-bdd.info#SerenityBDD @Wakaleo


OpenApplication.onTheJourneyPlannerPage(),
ChooseOrigin.of("Waterloo"),
ChooseDestination.of("Canary Wharf"),
ChooseTimeOfDeparture.of("09:00"),
Confirm.journeyDetails(),
seeThat(TheFastest.departureTime(), is("08:59"))
serenity-bdd.info#SerenityBDD @Wakaleo
Designing a task
serenity-bdd.info#SerenityBDD @Wakaleo
ChooseOrigin.of("Waterloo")
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
);

}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN)
);

}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
);

}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
);

}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.ORIGIN)
);

}
private final String station;
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.ORIGIN)
);

}
private final String station;
public ChooseOrigin(String origin) { this.station = origin;}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.ORIGIN)
);

}
private final String station;
public ChooseOrigin(String origin) { this.station = origin;}
public static Performable of(String station) {
return instrumented(ChooseOrigin.class, station); 

}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Step("{0} selects origin station of #station")
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.ORIGIN)
);

}
private final String station;
public ChooseOrigin(String origin) { this.station = origin;}
public static Performable of(String station) {
return instrumented(ChooseOrigin.class, station); 

}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Step("{0} selects origin station of #station")
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.ORIGIN)
);

}
private final String station;
public ChooseOrigin(String origin) { this.station = origin;}
public static Performable of(String station) {
return instrumented(ChooseOrigin.class, station); 

}
}
How the step appears in the reports
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Step("{0} selects origin station of #station")
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.ORIGIN)
);

}
private final String station;
public ChooseOrigin(String origin) { this.station = origin;}
public static Performable of(String station) {
return instrumented(ChooseOrigin.class, station); 

}
}
The name of the actor
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseOrigin implements Task {
@Step("{0} selects origin station of #station")
@Override
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(
Enter.theValue(station).into(JourneyPlanner.ORIGIN),
WaitUntil.the(JourneyPlanner.ORIGIN_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.ORIGIN)
);

}
private final String station;
public ChooseOrigin(String origin) { this.station = origin;}
public static Performable of(String station) {
return instrumented(ChooseOrigin.class, station); 

}
}
A member variable
serenity-bdd.info#SerenityBDD @Wakaleo
Or…
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseDestination {
/**
* Usage: ChooseDestination.of("Canary Wharf")
*/
public static Task of(String station) {
return Task.where("{0} selects origin station of #station",
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseDestination {
/**
* Usage: ChooseDestination.of("Canary Wharf")
*/
public static Task of(String station) {
return Task.where("{0} selects origin station of #station",
Enter.theValue(station).into(JourneyPlanner.DESTINATION),
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseDestination {
/**
* Usage: ChooseDestination.of("Canary Wharf")
*/
public static Task of(String station) {
return Task.where("{0} selects origin station of #station",
Enter.theValue(station).into(JourneyPlanner.DESTINATION),
WaitUntil.the(JourneyPlanner.DESTINATION_SUGGESTIONS, isVisible())
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseDestination {
/**
* Usage: ChooseDestination.of("Canary Wharf")
*/
public static Task of(String station) {
return Task.where("{0} selects origin station of #station",
Enter.theValue(station).into(JourneyPlanner.DESTINATION),
WaitUntil.the(JourneyPlanner.DESTINATION_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseDestination {
/**
* Usage: ChooseDestination.of("Canary Wharf")
*/
public static Task of(String station) {
return Task.where("{0} selects origin station of #station",
Enter.theValue(station).into(JourneyPlanner.DESTINATION),
WaitUntil.the(JourneyPlanner.DESTINATION_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.DESTINATION))
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class ChooseDestination {
/**
* Usage: ChooseDestination.of("Canary Wharf")
*/
public static Task of(String station) {
return Task.where("{0} selects origin station of #station",
Enter.theValue(station).into(JourneyPlanner.DESTINATION),
WaitUntil.the(JourneyPlanner.DESTINATION_SUGGESTIONS, isVisible())
.forNoMoreThan(10).seconds(),
PickFirstSuggestion.from(JourneyPlanner.DESTINATION))
.with(“station").of(station);
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
Interactions talk to the system
serenity-bdd.info#SerenityBDD @Wakaleo
Most WebDriver interactions come bundled with Serenity Screenplay
Enter.theValue(station).into(JourneyPlanner.ORIGIN)
Click.on(JourneyPlanner.PLAN_MY_JOURNEY)
SelectFromOptions.byVisibleText(timeOfDeparture)
.from(JourneyPlanner.SELECTED_DEPARTURE_TIME)
WaitUntil.the(JourneyResults.RESULTS_LIST, isVisible())
Scroll.to(JourneyResults.RESULTS_LIST)
serenity-bdd.info#SerenityBDD @Wakaleo
Actors perform tasks
serenity-bdd.info#SerenityBDD @Wakaleo
import net.serenitybdd.screenplay.Actor;
Actor connie = Actor.named("Connie");
serenity-bdd.info#SerenityBDD @Wakaleo
import net.serenitybdd.screenplay.Actor;
Actor connie = Actor.named("Connie");
connie.attemptsTo(
//…
);
serenity-bdd.info#SerenityBDD @Wakaleo
Actor connie = Actor.named("Connie");
connie.attemptsTo(
OpenApplication.onTheJourneyPlannerPage(),
ChooseOrigin.of("Waterloo"),
ChooseDestination.of("Canary Wharf"),
ChooseTimeOfDeparture.of(“09:00"),
Confirm.journeyDetails()
serenity-bdd.info#SerenityBDD @Wakaleo
Actors have abilities 

that enable interactions
with the system
serenity-bdd.info#SerenityBDD @Wakaleo
@Managed WebDriver browser;
Actor connie = Actor.named("Connie");
connie.can(BrowseTheWeb.with(browser));
serenity-bdd.info#SerenityBDD @Wakaleo
But where did the Page Objects go?
serenity-bdd.info#SerenityBDD @Wakaleo
public class JourneyPlanner {
public static final Target ORIGIN = Target.the("Departure station")
.locatedBy("#InputFrom");
public static final Target ORIGIN_SUGGESTIONS = Target.the("Origin Suggestions")
.locatedBy("//*[input[@id='InputFrom']]/*[contains(@class, 'tt-dropdown-menu')]");
public static final Target DESTINATION = Target.the("Destination station")
.locatedBy("#InputTo");
public static final Target DESTINATION_SUGGESTIONS = Target.the("Destination Suggestions")
.locatedBy("//*[input[@id='InputTo']]/*[contains(@class, 'tt-dropdown-menu')]");
public static final Target CHANGE_TIME = Target.the("Change Time")
.located(By.partialLinkText("change time"));
public static final Target LEAVING_BUTTON = Target.the("Leaving")
.located(By.cssSelector("label[for=departing]"));
public static final Target SELECTED_DEPARTURE_TIME = Target.the("Departure Time")
.located(By.id("Time"));
public static final Target SELECTED_DEPARTURE_DROPDOWN = Target.the("Departure Time")
.located(By.cssSelector(".hours"));
public static final Target PLAN_MY_JOURNEY = Target.the("Plan my journey")
.locatedBy("#plan-a-journey .plan-journey-button");;
}
serenity-bdd.info#SerenityBDD @Wakaleo
Target.the("Departure station").locatedBy("#InputFrom");
serenity-bdd.info#SerenityBDD @Wakaleo
Target.the("Departure station").locatedBy("#InputFrom");
How should this element be named in the reports?
serenity-bdd.info#SerenityBDD @Wakaleo
Target.the("Departure station").locatedBy("#InputFrom");
How should we locate it?
serenity-bdd.info#SerenityBDD @Wakaleo
What about more complicated cases?
serenity-bdd.info#SerenityBDD @Wakaleo
Target FILTER = Target.the(“filter")
.locatedBy("//*[@class='filters']//a[.='{0}']");
This value will be provided
serenity-bdd.info#SerenityBDD @Wakaleo
theActor.attemptsTo(FilterItems.toShow(“Completed"));
serenity-bdd.info#SerenityBDD @Wakaleo
public class FilterItems implements Task {
private final String filter;
public <T extends Actor> void performAs(T theActor) {
}
public static FilterItems toShow(String filter) {
return instrumented(FilterItems.class, status);
}
public FilterItems(TodoStatusFilter filter) { this.filter = filter; }
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class FilterItems implements Task {
private final String filter;
public <T extends Actor> void performAs(T theActor) {
theActor.attemptsTo(
Click.on(TodoList.FILTER
);
}
public static FilterItems toShow(String filter) {
return instrumented(FilterItems.class, status);
}
public FilterItems(TodoStatusFilter filter) { this.filter = filter; }
}
Click on the filter button
serenity-bdd.info#SerenityBDD @Wakaleo
public class FilterItems implements Task {
private final String filter;
public <T extends Actor> void performAs(T theActor) {
theActor.attemptsTo(
Click.on(TodoList.FILTER.of(filter)
);
}
public static FilterItems toShow(String filter) {
return instrumented(FilterItems.class, status);
}
public FilterItems(TodoStatusFilter filter) { this.filter = filter; }
}
Pass in the name of the filter
serenity-bdd.info#SerenityBDD @Wakaleo
public class FilterItems implements Task {
private final String filter;
public <T extends Actor> void performAs(T theActor) {
theActor.attemptsTo(
Click.on(TodoList.FILTER.of(filter)
.called(“filter by "+ filter))
);
}
public static FilterItems toShow(String filter) {
return instrumented(FilterItems.class, status);
}
public FilterItems(TodoStatusFilter filter) { this.filter = filter; }
How will this interaction appear in the reports?
serenity-bdd.info#SerenityBDD @Wakaleo
public class FilterItems implements Task {
private final String filter;
@Step("{0} filters items by #filter")
public <T extends Actor> void performAs(T theActor) {
theActor.attemptsTo(
Click.on(TodoList.FILTER.of(filter)
.called(“filter by "+ filter))
);
}
public static FilterItems toShow(String filter) {
return instrumented(FilterItems.class, status);
}
How will this task appear in the reports
serenity-bdd.info#SerenityBDD @Wakaleo
Actors ask questions
and verify the answers
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(the(JourneyResults.HEADER), isVisible())
);
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(the(JourneyResults.HEADER), isVisible())
);
A Consequence
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(the(JourneyResults.HEADER), isVisible())
);
A Question
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(the(JourneyResults.HEADER), isVisible())
);
A Hamcrest Matcher
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(the(JourneyResults.HEADER), isVisible())
);
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(textOf(JourneyResults.HEADER), is("Journey Results"))
);
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(selectedValueOf(JourneyPlanner.SELECTED_DEPARTURE_TIME),
is("Journey Results"))
);
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat("the fastest tube line”, TheFastest.tubeLine(),
is("Jubilee line to Canary Wharf”))
);
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat("the fastest tube line", TheFastest.tubeLine(),
is("Jubilee line to Canary Wharf")),
seeThat("the departure time", TheFastest.departureTime(),
is("08:59")),
seeThat("the arrival time", TheFastest.arrivalTime(),
is(“09:09"))
);
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat("the fastest tube line”, TheFastest.tubeLine(),
is("Jubilee line to Canary Wharf”))
);
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return new FastestTubeLineQuestion();
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return new FastestTubeLineQuestion();
}
}
public class FastestTubeLineQuestion implements Question<String> {
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return new FastestTubeLineQuestion();
}
}
public class FastestTubeLineQuestion implements Question<String> {
@Override
public String answeredBy(Actor actor) {
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return new FastestTubeLineQuestion();
}
}
public class FastestTubeLineQuestion implements Question<String> {
@Override
public String answeredBy(Actor actor) {
return Text.of(JourneyResults.ITINERARY)
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return new FastestTubeLineQuestion();
}
}
public class FastestTubeLineQuestion implements Question<String> {
@Override
public String answeredBy(Actor actor) {
return Text.of(JourneyResults.ITINERARY)
.viewedBy(actor)
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return new FastestTubeLineQuestion();
}
}
public class FastestTubeLineQuestion implements Question<String> {
@Override
public String answeredBy(Actor actor) {
return Text.of(JourneyResults.ITINERARY)
.viewedBy(actor)
.asString();
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return new FastestTubeLineQuestion();
}
}
@Subject("the fastest tube line")
public class FastestTubeLineQuestion implements Question<String> {
@Override
public String answeredBy(Actor actor) {
return Text.of(JourneyResults.ITINERARY)
.viewedBy(actor)
.asString();
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
Or…
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return actor -> Text.of(JourneyResults.ITINERARY)
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return actor -> Text.of(JourneyResults.ITINERARY)
.viewedBy(actor)
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class TheFastest {
public static Question<String> tubeLine() {
return actor -> Text.of(JourneyResults.ITINERARY)
.viewedBy(actor)
.asString();
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
connie.should(
seeThat(“all the tube lines”, AllThe.tubeLines(),
contains("Northern line to Moorgate”))
);
serenity-bdd.info#SerenityBDD @Wakaleo
public class AllThe {
public static Question<String> tubeLine() {
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class AllThe {
public static Question<String> tubeLine() {
return actor -> Text.of(JourneyResults.ITINERARY)
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class AllThe {
public static Question<String> tubeLine() {
return actor -> Text.of(JourneyResults.ITINERARY)
.viewedBy(actor)
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class AllThe {
public static Question<List<String>> tubeLine() {
return actor -> Text.of(JourneyResults.ITINERARY)
.viewedBy(actor)
.asList();
}
}
serenity-bdd.info#SerenityBDD @Wakaleo
The end result
serenity-bdd.info#SerenityBDD @Wakaleo
@Managed WebDriver browser;
@Test
public void should_show_the_soonest_departure_time_close_to_the_requested_time() {
Actor connie = Actor.named("Connie");
connie.can(BrowseTheWeb.with(browser));
connie.attemptsTo(
OpenApplication.onTheJourneyPlannerPage(),
ChooseOrigin.of("Waterloo"),
ChooseDestination.of("Canary Wharf"),
ChooseTimeOfDeparture.of("09:00"),
Confirm.journeyDetails()
);
connie.should(
seeThat("the fastest tube line", TheFastest tubeLine(),
is("Jubilee line to Canary Wharf")),
seeThat("the departure time", TheFastest.departureTime(), is("08:59")),
seeThat("the arrival time", TheFastest.arrivalTime(), is("09:09"))
);
}
serenity-bdd.info#SerenityBDD @Wakaleo
But wait! There’s more!
serenity-bdd.info#SerenityBDD @Wakaleo
@Managed WebDriver browser;
@Test
public void should_show_the_soonest_departure_time_close_to_the_requested_time() {
Actor connie = Actor.named("Connie");
connie.can(BrowseTheWeb.with(browser));
connie.attemptsTo(
OpenApplication.onTheJourneyPlannerPage(),
ChooseOrigin.of("Waterloo"),
ChooseDestination.of("Canary Wharf"),
ChooseTimeOfDeparture.of("09:00"),
Confirm.journeyDetails()
);
connie.should(
seeThat("the fastest tube line", TheFastest tubeLine(),
is("Jubilee line to Canary Wharf")),
seeThat("the departure time", TheFastest.departureTime(), is("08:59")),
seeThat("the arrival time", TheFastest.arrivalTime(), is("09:09"))
);
}
serenity-bdd.info#SerenityBDD @Wakaleo
@Managed WebDriver browser;
@Test
public void should_show_the_soonest_departure_time_close_to_the_requested_time() {
Actor connie = Actor.named("Connie");
connie.can(BrowseTheWeb.with(browser));
connie.attemptsTo(
PlanAJourney.from("Waterloo").to("Canary Wharf").departingAt("09:00")
);
connie.should(
seeThat("the fastest tube line", TheFastest tubeLine(),
is("Jubilee line to Canary Wharf")),
seeThat("the departure time", TheFastest.departureTime(), is("08:59")),
seeThat("the arrival time", TheFastest.arrivalTime(), is("09:09"))
);
}
serenity-bdd.info#SerenityBDD @Wakaleo
Working with Cucumber
serenity-bdd.info#SerenityBDD @Wakaleo
Feature: Plan a new journey
Scenario: Plan a new journey
Given that Connie is a London commuter
When she plans a journey from Waterloo to Canary Wharf departing at 09:00
Then she should see that the fastest train departs at 08:59
serenity-bdd.info#SerenityBDD @Wakaleo
public class PlanAJourneyStepDefinitions {
@Before
public void set_the_stage() {
OnStage.setTheStage(new OnlineCast());
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class PlanAJourneyStepDefinitions {
@Before
public void set_the_stage() {
OnStage.setTheStage(new OnlineCast());
}
@Given("^that (.*) is a London commuter$")
public void a_london_commuter_named(String commuterName) throws Throwable {
theActorCalled(commuterName);
}
serenity-bdd.info#SerenityBDD @Wakaleo
public class PlanAJourneyStepDefinitions {
@Before
public void set_the_stage() {
OnStage.setTheStage(new OnlineCast());
}
@Given("^that (.*) is a London commuter$")
public void a_london_commuter_named(String commuterName) throws Throwable {
theActorCalled(commuterName);
}
serenity-bdd.info#SerenityBDD @Wakaleo
Feature: Plan a new journey
Scenario: Plan a new journey
Given that Connie is a London commuter
When she plans a journey from Waterloo to Canary Wharf departing at 09:00
Then she should see that the fastest train departs at 08:59
serenity-bdd.info#SerenityBDD @Wakaleo
public class PlanAJourneyStepDefinitions {
@Before
public void set_the_stage() {
OnStage.setTheStage(new OnlineCast());
}
@Given("^that (.*) is a London commuter$")
public void a_london_commuter_named(String commuterName) throws Throwable {
theActorCalled(commuterName);
}
@When("^(.*) plans a journey from (.*) to (.*) departing at (.*)$")
public void plan_a_journeys(String name, String departure, String destination,
String departureTime) throws Throwable {
theActorCalled(name).attemptsTo(
PlanAJourney.from(departure).to(destination).departingAt(departureTime)
);
}
serenity-bdd.info#SerenityBDD @Wakaleo
Feature: Plan a new journey
Scenario: Plan a new journey
Given that Connie is a London commuter
When she plans a journey from Waterloo to Canary Wharf departing at 09:00
Then she should see that the fastest train departs at 08:59
serenity-bdd.info#SerenityBDD @Wakaleo
public class PlanAJourneyStepDefinitions {
@Before
public void set_the_stage() {
OnStage.setTheStage(new OnlineCast());
}
@Given("^that (.*) is a London commuter$")
public void a_london_commuter_named(String commuterName) throws Throwable {…}
@When("^(.*) plans a journey from (.*) to (.*) departing at (.*)$")
public void plan_a_journeys(String name, String departure, String destination,
String departureTime) throws Throwable {…}
@Then("^(.*) should see that the fastest train departs at (.*)$")
public void should_see_departure_time(String name,
String expectedDepartureTime) throws Throwable {
theActorCalled(name).should(
seeThat("the departure time", TheFastest.departureTime(),
is(expectedDepartureTime))
);
serenity-bdd.info#SerenityBDD @Wakaleo
Cucumber gives us flexibility
serenity-bdd.info#SerenityBDD @Wakaleo
Feature: Plan a new journey
Scenario Outline: Plan many a journey
Given that Connie is a London commuter
When she plans a journey from <departure> to <destination> departing at <plannedDepartureTime>
Then she should see a trip on the <line> line departing at <departureTime>
Examples:
| departure | destination | line | plannedDepartureTime | departureTime |
| Waterloo | Canary Wharf | Jubilee line to Canary Wharf | 09:00 | 08:59 |
| London Bridge | Moorgate | Northern line to Moorgate | 10:00 | 09:59 |
serenity-bdd.info#SerenityBDD @Wakaleo
Feature: Plan a new journey
Scenario Outline: Plan many a journey
Given that Connie is a London commuter
When she plans a journey from <departure> to <destination> departing at <plannedDepartureTime>
Then she should see a trip on the <line> line departing at <departureTime>
Examples:
| departure | destination | line | plannedDepartureTime | departureTime |
| Waterloo | Canary Wharf | Jubilee line to Canary Wharf | 09:00 | 08:59 |
| London Bridge | Moorgate | Northern line to Moorgate | 10:00 | 09:59 |
serenity-bdd.info#SerenityBDD @Wakaleo
@Then("^(.*) should see a trip on the (.*) line departing at (.*)")
public void should_see_trip(String name, String line, String departureTime) {
theActorCalled(name).should(
seeThat("the fastest tube line”, TheFastest.tubeLine(),
is(line)),
seeThat("the departure time", TheFastest.departureTime(),
is(departureTime))
);
}
serenity-bdd.info#SerenityBDD @Wakaleo
Scenario: Coordinating journeys
Given that Connie is a London commuter
And that Charles is a London commuter
When Connie plans a journey from Waterloo to Canary Wharf departing at 09:00
And Charles plans a journey from London Bridge to Moorgate departing at 10:00
Then Connie should see a trip on the Jubilee line to Canary Wharf line departing at 08:59
And Charles should see a trip on the Northern line to Moorgate line departing at 09:59
serenity-bdd.info#SerenityBDD @Wakaleo
Obsess about reporting!
serenity-bdd.info#SerenityBDD @Wakaleo
Levels of communication
-
+
serenity-bdd.info#SerenityBDD @Wakaleo
-
+
10,000 ft

Release Readiness
serenity-bdd.info#SerenityBDD @Wakaleo
-
+
1000 ft

Capabilities and Features
serenity-bdd.info#SerenityBDD @Wakaleo
-
+
100 ft

Scenarios and Steps
serenity-bdd.info#SerenityBDD @Wakaleo
-
+
Release
Readiness
serenity-bdd.info#SerenityBDD @Wakaleo
-
+
Release
Readiness
Capabilities
serenity-bdd.info#SerenityBDD @Wakaleo
-
+
Release
Readiness
Capabilities
Features
serenity-bdd.info#SerenityBDD @Wakaleo
-
+
Release
Readiness
Capabilities
Features
Scenarios
serenity-bdd.info#SerenityBDD @Wakaleo
Questions to ask:
- How much time do I spend maintaining the tests?
- Am I modelling user’s interactions with the system, 

or how the system is built?
- Can I share elements of my testing framework with others?
- How much duplication and dead code is there in my tests?
- How long would it take me to find the issue if a test fails?
- How long to change the tests when the system changes?
- Can I use my tests to drive the design of the system?
serenity-bdd.info#SerenityBDD @Wakaleo
Make your tests

scalable by design
serenity-bdd.info#SerenityBDD @Wakaleo
Prefer JavaScript?
Try
http://serenity-js.org
serenity-bdd.info#SerenityBDD @Wakaleo
Jan Molak
Original slides and demonstration in JavaScript designed by
consultant, trainer, lead developer of Serenity/JS
serenity-bdd.info#SerenityBDD @Wakaleo
Thank you!
Learn more at serenity-bdd.info



Source code:
How we can help:

Get in touch:
Learn Serenity BDD online




bit.ly/serentiy-planner
johnfergusonsmart.com

reachme@johnfergusonsmart.com
http://serenity-dojo.com

More Related Content

What's hot

BDD testing with cucumber
BDD testing with cucumberBDD testing with cucumber
BDD testing with cucumberDaniel Kummer
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with ReduxVedran Blaženka
 
Codemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootCodemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootIván López Martín
 
API Test Automation Using Karate (Anil Kumar Moka)
API Test Automation Using Karate (Anil Kumar Moka)API Test Automation Using Karate (Anil Kumar Moka)
API Test Automation Using Karate (Anil Kumar Moka)Peter Thomas
 
Test Automation Framework using Cucumber BDD overview (part 1)
Test Automation Framework using Cucumber BDD overview (part 1)Test Automation Framework using Cucumber BDD overview (part 1)
Test Automation Framework using Cucumber BDD overview (part 1)Mindfire Solutions
 
Resume Andreas Chandra_online.pdf
Resume Andreas Chandra_online.pdfResume Andreas Chandra_online.pdf
Resume Andreas Chandra_online.pdfAndreas Chandra
 
Testing of React JS app
Testing of React JS appTesting of React JS app
Testing of React JS appAleks Zinevych
 
What Is Cucumber?
What Is Cucumber?What Is Cucumber?
What Is Cucumber?QATestLab
 
Specification-By-Example with Gherkin
Specification-By-Example with GherkinSpecification-By-Example with Gherkin
Specification-By-Example with GherkinChristian Hassa
 
Test cases and bug report v3.2
Test cases and bug report v3.2Test cases and bug report v3.2
Test cases and bug report v3.2Andrey Oleynik
 
Bdd – with cucumber and gherkin
Bdd – with cucumber and gherkinBdd – with cucumber and gherkin
Bdd – with cucumber and gherkinArati Joshi
 
DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...
DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...
DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...Edureka!
 
BDD in Action – principles, practices and real-world application
BDD in Action – principles, practices and real-world applicationBDD in Action – principles, practices and real-world application
BDD in Action – principles, practices and real-world applicationJohn Ferguson Smart Limited
 
Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...
Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...
Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...Edureka!
 
Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...
Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...
Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...Moataz Nabil
 
Karate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made SimpleKarate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made SimpleVodqaBLR
 

What's hot (20)

BDD testing with cucumber
BDD testing with cucumberBDD testing with cucumber
BDD testing with cucumber
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with Redux
 
Mobile testing
Mobile testingMobile testing
Mobile testing
 
Codemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootCodemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring Boot
 
API Test Automation Using Karate (Anil Kumar Moka)
API Test Automation Using Karate (Anil Kumar Moka)API Test Automation Using Karate (Anil Kumar Moka)
API Test Automation Using Karate (Anil Kumar Moka)
 
Test Automation Framework with BDD and Cucumber
Test Automation Framework with BDD and CucumberTest Automation Framework with BDD and Cucumber
Test Automation Framework with BDD and Cucumber
 
Cucumber ppt
Cucumber pptCucumber ppt
Cucumber ppt
 
Test Automation Framework using Cucumber BDD overview (part 1)
Test Automation Framework using Cucumber BDD overview (part 1)Test Automation Framework using Cucumber BDD overview (part 1)
Test Automation Framework using Cucumber BDD overview (part 1)
 
Resume Andreas Chandra_online.pdf
Resume Andreas Chandra_online.pdfResume Andreas Chandra_online.pdf
Resume Andreas Chandra_online.pdf
 
Testing of React JS app
Testing of React JS appTesting of React JS app
Testing of React JS app
 
What Is Cucumber?
What Is Cucumber?What Is Cucumber?
What Is Cucumber?
 
Building MAUI UI in C#.pptx
Building MAUI UI in C#.pptxBuilding MAUI UI in C#.pptx
Building MAUI UI in C#.pptx
 
Specification-By-Example with Gherkin
Specification-By-Example with GherkinSpecification-By-Example with Gherkin
Specification-By-Example with Gherkin
 
Test cases and bug report v3.2
Test cases and bug report v3.2Test cases and bug report v3.2
Test cases and bug report v3.2
 
Bdd – with cucumber and gherkin
Bdd – with cucumber and gherkinBdd – with cucumber and gherkin
Bdd – with cucumber and gherkin
 
DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...
DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...
DevOps Testing | Continuous Testing In DevOps | DevOps Tutorial | DevOps Trai...
 
BDD in Action – principles, practices and real-world application
BDD in Action – principles, practices and real-world applicationBDD in Action – principles, practices and real-world application
BDD in Action – principles, practices and real-world application
 
Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...
Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...
Selenium Tutorial For Beginners | What Is Selenium? | Selenium Automation Tes...
 
Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...
Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...
Build your QA Pipeline using Serenity , Selenium WebDriver , Rest Assured and...
 
Karate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made SimpleKarate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made Simple
 

More from John Ferguson Smart Limited

My Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin ScenariosMy Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin ScenariosJohn Ferguson Smart Limited
 
Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...John Ferguson Smart Limited
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceJohn Ferguson Smart Limited
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceJohn Ferguson Smart Limited
 
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...John Ferguson Smart Limited
 
Screenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testingScreenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testingJohn Ferguson Smart Limited
 
All the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practicesAll the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practicesJohn Ferguson Smart Limited
 
It's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for TestersIt's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for TestersJohn Ferguson Smart Limited
 
BDD in Action - Automated Web Testing with WebDriver and Serenity
BDD in Action - Automated Web Testing with WebDriver and SerenityBDD in Action - Automated Web Testing with WebDriver and Serenity
BDD in Action - Automated Web Testing with WebDriver and SerenityJohn Ferguson Smart Limited
 

More from John Ferguson Smart Limited (20)

My Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin ScenariosMy Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin Scenarios
 
Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a difference
 
BE A POD OF DOLPHINS, NOT A DANCING ELEPHANT
BE A POD OF DOLPHINS, NOT A DANCING ELEPHANTBE A POD OF DOLPHINS, NOT A DANCING ELEPHANT
BE A POD OF DOLPHINS, NOT A DANCING ELEPHANT
 
Feature Mapping Workshop
Feature Mapping WorkshopFeature Mapping Workshop
Feature Mapping Workshop
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a difference
 
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
 
Shift left-devoxx-pl
Shift left-devoxx-plShift left-devoxx-pl
Shift left-devoxx-pl
 
Screenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testingScreenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testing
 
Cucumber and Spock Primer
Cucumber and Spock PrimerCucumber and Spock Primer
Cucumber and Spock Primer
 
All the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practicesAll the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practices
 
CukeUp 2016 Agile Product Planning Workshop
CukeUp 2016 Agile Product Planning WorkshopCukeUp 2016 Agile Product Planning Workshop
CukeUp 2016 Agile Product Planning Workshop
 
BDD Anti-patterns
BDD Anti-patternsBDD Anti-patterns
BDD Anti-patterns
 
Serenity and the Journey Pattern
Serenity and the Journey PatternSerenity and the Journey Pattern
Serenity and the Journey Pattern
 
BDD - Collaborate like you mean it!
BDD - Collaborate like you mean it!BDD - Collaborate like you mean it!
BDD - Collaborate like you mean it!
 
BDD-Driven Microservices
BDD-Driven MicroservicesBDD-Driven Microservices
BDD-Driven Microservices
 
BDD Anti-patterns
BDD Anti-patternsBDD Anti-patterns
BDD Anti-patterns
 
It's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for TestersIt's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for Testers
 
BDD in Action - Automated Web Testing with WebDriver and Serenity
BDD in Action - Automated Web Testing with WebDriver and SerenityBDD in Action - Automated Web Testing with WebDriver and Serenity
BDD in Action - Automated Web Testing with WebDriver and Serenity
 
BDD in Action - Devoxx 2014
BDD in Action - Devoxx 2014BDD in Action - Devoxx 2014
BDD in Action - Devoxx 2014
 

Recently uploaded

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 

Recently uploaded (20)

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 

Sustainable Test Automation with Serenity BDD and Screenplay