Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Sustainable Test Automation with Serenity BDD and Screenplay

1,143 views

Published on

As projects get faster and teams get leaner, the need to write high quality automated acceptance criteria quickly and efficiently has never been greater. Engineers in Test simply cannot afford to spend time maintaining brittle tests. And yet, without solid test automation strategies, this is what many teams find themselves doing. In this workshop, you will learn a better way. You will learn how to write clean, clear and maintainable tests using the Screenplay Pattern, an innovative new approach to writing BDD-style automated acceptance tests that are easier to understand, easier to extend and easier to maintain. The workshop will be a practical demonstration of the principles of good automated test design. There will be live coding of real-world BDD automated acceptance tests in abundance, using Java, Serenity BDD and Cucumber. We will go from requirements and BDD-style Acceptance Criteria in Cucumber right through to automated acceptance tests and living documentation.

Published in: Technology

Sustainable Test Automation with Serenity BDD and Screenplay

  1. 1. serenity-bdd.info#SerenityBDD @Wakaleo Sustainable Test Automation with Serenity BDD and Screenplay
  2. 2. serenity-bdd.info#SerenityBDD @Wakaleo John Ferguson Smart Author of ‘BDD in Action’
 Lead Developer of Serenity BDD Founder of the Serenity Dojo
  3. 3. serenity-bdd.info#SerenityBDD @Wakaleo
  4. 4. serenity-bdd.info#SerenityBDD @Wakaleo
  5. 5. serenity-bdd.info#SerenityBDD @Wakaleo Want to follow along? bit.ly/serentiy-planner
  6. 6. serenity-bdd.info#SerenityBDD @Wakaleo We ❤ E2E
  7. 7. serenity-bdd.info#SerenityBDD @Wakaleo We 💔 E2E (said no one ever)
  8. 8. serenity-bdd.info#SerenityBDD @Wakaleo Execution time: minutes → hours
  9. 9. serenity-bdd.info#SerenityBDD @Wakaleo Execution time: minutes → hours Writing: hours → days
  10. 10. serenity-bdd.info#SerenityBDD @Wakaleo Execution time: minutes → hours Writing: hours → days Analysis and reporting: hours → days
  11. 11. serenity-bdd.info#SerenityBDD @Wakaleo Execution time: minutes → hours Writing: hours → days Analysis and reporting: hours → days Maintenance: days → weeks
  12. 12. serenity-bdd.info#SerenityBDD @Wakaleo The difference between
 scalable tests 
 and a world of pain
 is design.
  13. 13. serenity-bdd.info#SerenityBDD @Wakaleo Testing the Journey Planner
  14. 14. #SerenityBDD @Wakaleo serenity-bdd.info
  15. 15. #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.
  16. 16. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
  17. 17. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo
  18. 18. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo Enter “Waterloo”
  19. 19. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo Enter “Waterloo” Pick the first suggestion
  20. 20. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo Enter “Waterloo” Pick the first suggestion Wait for the suggestions
  21. 21. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo Enter “Waterloo” Pick the first suggestion Wait for the suggestions Press ▼
  22. 22. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo Enter “Waterloo” Pick the first suggestion Wait for the suggestions Press ▼
 Press ↩
  23. 23. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo Enter “Waterloo” Pick the first suggestion Wait for the suggestions Press ▼
 Press ↩
  24. 24. #SerenityBDD @Wakaleo serenity-bdd.info Open the Journey Planner
 Choose origin: Waterloo
  25. 25. serenity-bdd.info#SerenityBDD @Wakaleo Open the Journey Planner
 Choose origin: Waterloo Choose destination: Canary Wharf
  26. 26. serenity-bdd.info#SerenityBDD @Wakaleo Open the Journey Planner
 Choose origin: Waterloo Choose destination: Canary Wharf
  27. 27. serenity-bdd.info#SerenityBDD @Wakaleo Open the Journey Planner
 Choose origin: Waterloo Choose destination: Canary Wharf Choose time of departure: 9:00 AM
  28. 28. 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
  29. 29. 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
  30. 30. 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”
  31. 31. serenity-bdd.info#SerenityBDD @Wakaleo Open the Journey Planner
 Choose origin: Waterloo Choose destination: Canary Wharf Choose time of departure: 9:00 AM
  32. 32. 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
  33. 33. 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”
  34. 34. 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
  35. 35. 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
  36. 36. 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
  37. 37. serenity-bdd.info#SerenityBDD @Wakaleo At a high level, it’s just 6 tasks
  38. 38. serenity-bdd.info#SerenityBDD @Wakaleo Scripting the flow
  39. 39. 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");
  40. 40. serenity-bdd.info#SerenityBDD @Wakaleo browser.get("https://tfl.gov.uk/");
  41. 41. serenity-bdd.info#SerenityBDD @Wakaleo browser.get(“https://tfl.gov.uk/"); browser.findElement(origin).sendKeys("Waterloo");
  42. 42. 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));
  43. 43. 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);
  44. 44. 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");
  45. 45. 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);
  46. 46. serenity-bdd.info#SerenityBDD @Wakaleo … browser.findElement(changeTimeLink).click(); browser.findElement(leavingButton).click(); new Select(browser.findElement(timeSelector)) .selectByVisibleText("09:00");
  47. 47. 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));
  48. 48. serenity-bdd.info#SerenityBDD @Wakaleo … String fastestDepartureTime = startTimeOf(browser.findElement(fastestJourney).getText()); assertThat(fastestDepartureTime).isEqualTo("08:59");
  49. 49. 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 ?
  50. 50. 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());
  51. 51. #SerenityBDD @Wakaleo “ serenity-bdd.info If you have WebDriver APIs in your test methods, You’re Doing It Wrong! - Simon Stewart, creator of WebDriver
  52. 52. serenity-bdd.info#SerenityBDD @Wakaleo A test system needs design
  53. 53. serenity-bdd.info#SerenityBDD @Wakaleo Page Objects to the rescue?
  54. 54. serenity-bdd.info#SerenityBDD @Wakaleo
  55. 55. serenity-bdd.info#SerenityBDD @Wakaleo @DefaultUrl(“https://tfl.gov.uk") public class JourneyPlannerPage extends PageObject { // Locators // Actions }
  56. 56. 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; …
  57. 57. 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() {…} }
  58. 58. serenity-bdd.info#SerenityBDD @Wakaleo @RunWith(SerenityRunner.class) public class JourneyPlannerWithPageObjectsTest { 

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

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

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

  62. 62. 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");
  63. 63. 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!
  64. 64. 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"); } }
  65. 65. 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
  66. 66. 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)); }
  67. 67. 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)); }
  68. 68. serenity-bdd.info#SerenityBDD @Wakaleo Four Software Engineering Principles can help you write more maintainable tests
  69. 69. serenity-bdd.info#SerenityBDD @Wakaleo 1) Separation of Concerns Hide complexity through layers of abstraction
  70. 70. serenity-bdd.info#SerenityBDD @Wakaleo 2) Single Responsibility Principle A class should have only one reason to change
  71. 71. serenity-bdd.info#SerenityBDD @Wakaleo 3) Open-Closed Principle Green diffs
  72. 72. serenity-bdd.info#SerenityBDD @Wakaleo 4) Narrative coding style Express your intent
  73. 73. serenity-bdd.info#SerenityBDD @Wakaleo Serenity BDD and the Screenplay Pattern
  74. 74. #SerenityBDD @Wakaleo “ serenity-bdd.info Your test framework should not model how your system behaves. It should model how your user behaves.
  75. 75. serenity-bdd.info#SerenityBDD @Wakaleo
  76. 76. serenity-bdd.info#SerenityBDD @Wakaleo Goals What are you trying to achieve?
  77. 77. serenity-bdd.info#SerenityBDD @Wakaleo Goals What are you trying to achieve? Tasks What do you need to do to achieve this goal?
  78. 78. 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?
  79. 79. serenity-bdd.info#SerenityBDD @Wakaleo Actor Connor the commuter
  80. 80. serenity-bdd.info#SerenityBDD @Wakaleo Plan my journey Actor Goals Connor the commuter
  81. 81. serenity-bdd.info#SerenityBDD @Wakaleo Plan my journey Actor Goals Tasks Choose Destination Choose Origin Choose time of departure Confirm selection Connor the commuter
  82. 82. 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
  83. 83. serenity-bdd.info#SerenityBDD @Wakaleo Say what you mean
  84. 84. 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
  85. 85. 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"))
  86. 86. serenity-bdd.info#SerenityBDD @Wakaleo Designing a task
  87. 87. serenity-bdd.info#SerenityBDD @Wakaleo ChooseOrigin.of("Waterloo")
  88. 88. serenity-bdd.info#SerenityBDD @Wakaleo public class ChooseOrigin implements Task { }
  89. 89. serenity-bdd.info#SerenityBDD @Wakaleo public class ChooseOrigin implements Task { @Override public <T extends Actor> void performAs(T actor) {} }
  90. 90. serenity-bdd.info#SerenityBDD @Wakaleo public class ChooseOrigin implements Task { @Override public <T extends Actor> void performAs(T actor) { actor.attemptsTo( );
 } }
  91. 91. 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) );
 } }
  92. 92. 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()) );
 } }
  93. 93. 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(), );
 } }
  94. 94. 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; }
  95. 95. 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;} }
  96. 96. 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); 
 } }
  97. 97. 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); 
 } }
  98. 98. 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
  99. 99. 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
  100. 100. 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
  101. 101. serenity-bdd.info#SerenityBDD @Wakaleo Or…
  102. 102. 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", } }
  103. 103. 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), } }
  104. 104. 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()) } }
  105. 105. 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(), } }
  106. 106. 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)) } }
  107. 107. 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); } }
  108. 108. serenity-bdd.info#SerenityBDD @Wakaleo Interactions talk to the system
  109. 109. 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)
  110. 110. serenity-bdd.info#SerenityBDD @Wakaleo Actors perform tasks
  111. 111. serenity-bdd.info#SerenityBDD @Wakaleo import net.serenitybdd.screenplay.Actor; Actor connie = Actor.named("Connie");
  112. 112. serenity-bdd.info#SerenityBDD @Wakaleo import net.serenitybdd.screenplay.Actor; Actor connie = Actor.named("Connie"); connie.attemptsTo( //… );
  113. 113. 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()
  114. 114. serenity-bdd.info#SerenityBDD @Wakaleo Actors have abilities 
 that enable interactions with the system
  115. 115. serenity-bdd.info#SerenityBDD @Wakaleo @Managed WebDriver browser; Actor connie = Actor.named("Connie"); connie.can(BrowseTheWeb.with(browser));
  116. 116. serenity-bdd.info#SerenityBDD @Wakaleo But where did the Page Objects go?
  117. 117. 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");; }
  118. 118. serenity-bdd.info#SerenityBDD @Wakaleo Target.the("Departure station").locatedBy("#InputFrom");
  119. 119. serenity-bdd.info#SerenityBDD @Wakaleo Target.the("Departure station").locatedBy("#InputFrom"); How should this element be named in the reports?
  120. 120. serenity-bdd.info#SerenityBDD @Wakaleo Target.the("Departure station").locatedBy("#InputFrom"); How should we locate it?
  121. 121. serenity-bdd.info#SerenityBDD @Wakaleo What about more complicated cases?
  122. 122. serenity-bdd.info#SerenityBDD @Wakaleo Target FILTER = Target.the(“filter") .locatedBy("//*[@class='filters']//a[.='{0}']"); This value will be provided
  123. 123. serenity-bdd.info#SerenityBDD @Wakaleo theActor.attemptsTo(FilterItems.toShow(“Completed"));
  124. 124. 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; } }
  125. 125. 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
  126. 126. 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
  127. 127. 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?
  128. 128. 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
  129. 129. serenity-bdd.info#SerenityBDD @Wakaleo Actors ask questions and verify the answers
  130. 130. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(the(JourneyResults.HEADER), isVisible()) );
  131. 131. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(the(JourneyResults.HEADER), isVisible()) ); A Consequence
  132. 132. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(the(JourneyResults.HEADER), isVisible()) ); A Question
  133. 133. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(the(JourneyResults.HEADER), isVisible()) ); A Hamcrest Matcher
  134. 134. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(the(JourneyResults.HEADER), isVisible()) );
  135. 135. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(textOf(JourneyResults.HEADER), is("Journey Results")) );
  136. 136. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(selectedValueOf(JourneyPlanner.SELECTED_DEPARTURE_TIME), is("Journey Results")) );
  137. 137. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat("the fastest tube line”, TheFastest.tubeLine(), is("Jubilee line to Canary Wharf”)) );
  138. 138. 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")) );
  139. 139. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat("the fastest tube line”, TheFastest.tubeLine(), is("Jubilee line to Canary Wharf”)) );
  140. 140. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { }
  141. 141. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { public static Question<String> tubeLine() { } }
  142. 142. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { public static Question<String> tubeLine() { return new FastestTubeLineQuestion(); } }
  143. 143. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { public static Question<String> tubeLine() { return new FastestTubeLineQuestion(); } } public class FastestTubeLineQuestion implements Question<String> { }
  144. 144. 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) { } }
  145. 145. 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) } }
  146. 146. 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) } }
  147. 147. 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(); } }
  148. 148. 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(); } }
  149. 149. serenity-bdd.info#SerenityBDD @Wakaleo Or…
  150. 150. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { }
  151. 151. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { public static Question<String> tubeLine() { } }
  152. 152. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { public static Question<String> tubeLine() { return actor -> Text.of(JourneyResults.ITINERARY) } }
  153. 153. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { public static Question<String> tubeLine() { return actor -> Text.of(JourneyResults.ITINERARY) .viewedBy(actor) } }
  154. 154. serenity-bdd.info#SerenityBDD @Wakaleo public class TheFastest { public static Question<String> tubeLine() { return actor -> Text.of(JourneyResults.ITINERARY) .viewedBy(actor) .asString(); } }
  155. 155. serenity-bdd.info#SerenityBDD @Wakaleo connie.should( seeThat(“all the tube lines”, AllThe.tubeLines(), contains("Northern line to Moorgate”)) );
  156. 156. serenity-bdd.info#SerenityBDD @Wakaleo public class AllThe { public static Question<String> tubeLine() { } }
  157. 157. serenity-bdd.info#SerenityBDD @Wakaleo public class AllThe { public static Question<String> tubeLine() { return actor -> Text.of(JourneyResults.ITINERARY) } }
  158. 158. serenity-bdd.info#SerenityBDD @Wakaleo public class AllThe { public static Question<String> tubeLine() { return actor -> Text.of(JourneyResults.ITINERARY) .viewedBy(actor) } }
  159. 159. serenity-bdd.info#SerenityBDD @Wakaleo public class AllThe { public static Question<List<String>> tubeLine() { return actor -> Text.of(JourneyResults.ITINERARY) .viewedBy(actor) .asList(); } }
  160. 160. serenity-bdd.info#SerenityBDD @Wakaleo The end result
  161. 161. 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")) ); }
  162. 162. serenity-bdd.info#SerenityBDD @Wakaleo But wait! There’s more!
  163. 163. 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")) ); }
  164. 164. 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")) ); }
  165. 165. serenity-bdd.info#SerenityBDD @Wakaleo Working with Cucumber
  166. 166. 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
  167. 167. serenity-bdd.info#SerenityBDD @Wakaleo public class PlanAJourneyStepDefinitions { @Before public void set_the_stage() { OnStage.setTheStage(new OnlineCast()); }
  168. 168. 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); }
  169. 169. 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); }
  170. 170. 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
  171. 171. 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) ); }
  172. 172. 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
  173. 173. 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)) );
  174. 174. serenity-bdd.info#SerenityBDD @Wakaleo Cucumber gives us flexibility
  175. 175. 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 |
  176. 176. 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 |
  177. 177. 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)) ); }
  178. 178. 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
  179. 179. serenity-bdd.info#SerenityBDD @Wakaleo Obsess about reporting!
  180. 180. serenity-bdd.info#SerenityBDD @Wakaleo Levels of communication - +
  181. 181. serenity-bdd.info#SerenityBDD @Wakaleo - + 10,000 ft
 Release Readiness
  182. 182. serenity-bdd.info#SerenityBDD @Wakaleo - + 1000 ft
 Capabilities and Features
  183. 183. serenity-bdd.info#SerenityBDD @Wakaleo - + 100 ft
 Scenarios and Steps
  184. 184. serenity-bdd.info#SerenityBDD @Wakaleo - + Release Readiness
  185. 185. serenity-bdd.info#SerenityBDD @Wakaleo - + Release Readiness Capabilities
  186. 186. serenity-bdd.info#SerenityBDD @Wakaleo - + Release Readiness Capabilities Features
  187. 187. serenity-bdd.info#SerenityBDD @Wakaleo - + Release Readiness Capabilities Features Scenarios
  188. 188. 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?
  189. 189. serenity-bdd.info#SerenityBDD @Wakaleo Make your tests
 scalable by design
  190. 190. serenity-bdd.info#SerenityBDD @Wakaleo Prefer JavaScript? Try http://serenity-js.org
  191. 191. serenity-bdd.info#SerenityBDD @Wakaleo Jan Molak Original slides and demonstration in JavaScript designed by consultant, trainer, lead developer of Serenity/JS
  192. 192. 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

×