SlideShare a Scribd company logo
The Screenplay Pattern
Better Interactions for Better Automation
2
Andrew “Pandy” Knight
Lead Software Engineer in Test
PrecisionLender, a Q2 Company
AutomationPanda.com
@AutomationPanda
3
Andrew “Pandy” Knight
Lead Developer for Boa Constrictor
4
3-Part Talk
1. Problems with traditional interactions
2. Why Screenplay is better
3. Using Screenplay with Boa Constrictor
Interactions
Interactions
How users operate software: loads, clicks, scrapes, etc.
8
Testing =
Interaction +Verification
9
A simple DuckDuckGo search test
Open the
search engine
Search for a
phrase
Verify results
appear
10
A simple DuckDuckGo search test
Open the
search engine
Search for a
phrase
Verify results
appear
1. Navigate
11
A simple DuckDuckGo search test
Open the
search engine
Search for a
phrase
Verify results
appear
1. Navigate
1. Enter search phrase
2. Click search button
12
A simple DuckDuckGo search test
Open the
search engine
Search for a
phrase
Verify results
appear
1. Navigate
1. Enter search phrase
2. Click search button
1. Scrape page title
2. Scrape result links
13
14
Raw WebDriver calls
15
Raw WebDriver calls
IWebDriver driver = new ChromeDriver();
16
Raw WebDriver calls
IWebDriver driver = new ChromeDriver();
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
17
Raw WebDriver calls
IWebDriver driver = new ChromeDriver();
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
// Search for a phrase
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
18
Raw WebDriver calls
IWebDriver driver = new ChromeDriver();
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
// Search for a phrase
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
// Verify results appear
driver.Title.ToLower().Should().Contain("panda");
driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0);
19
Raw WebDriver calls
IWebDriver driver = new ChromeDriver();
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
// Search for a phrase
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
// Verify results appear
driver.Title.ToLower().Should().Contain("panda");
driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0);
driver.Quit();
20
Raw WebDriver calls
IWebDriver driver = new ChromeDriver();
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
// Search for a phrase
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
// Verify results appear
driver.Title.ToLower().Should().Contain("panda");
driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0);
driver.Quit();
Race
Conditions
!
1
2
3
21
Implicit waits
IWebDriver driver = new ChromeDriver();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
// Search for a phrase
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
// Verify results appear
driver.Title.ToLower().Should().Contain("panda");
driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0);
driver.Quit();
22
Explicit waits
IWebDriver driver = new ChromeDriver();
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
// Search for a phrase
wait.Until(d => d.FindElements(By.Id("search_form_input_homepage")).Count > 0);
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
// Verify results appear
wait.Until(d => d.Title.ToLower().Contains("panda"));
wait.Until(d => d.FindElements(By.CssSelector("a.result__a"))).Count > 0);
driver.Quit();
23
Duplicate code
IWebDriver driver = new ChromeDriver();
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
// Open the search engine
driver.Navigate().GoToUrl("https://duckduckgo.com/");
// Search for a phrase
wait.Until(d => d.FindElements(By.Id("search_form_input_homepage")).Count > 0);
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
// Verify results appear
wait.Until(d => d.Title.ToLower().Contains("panda"));
wait.Until(d => d.FindElements(By.CssSelector("a.result__a"))).Count > 0);
driver.Quit();
24
Unintuitive code
IWebDriver driver = new ChromeDriver();
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
driver.Navigate().GoToUrl("https://duckduckgo.com/");
wait.Until(d => d.FindElements(By.Id("search_form_input_homepage")).Count > 0);
driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda");
driver.FindElement(By.Id("search_button_homepage")).Click();
wait.Until(d => d.Title.ToLower().Contains("panda"));
wait.Until(d => d.FindElements(By.CssSelector("a.result__a"))).Count > 0);
driver.Quit();
25
Page Object Pattern
public class SearchPage
{
}
26
Page Object Pattern
public class SearchPage
{
public const string Url = "https://duckduckgo.com/";
public static By SearchInput => By.Id("search_form_input_homepage");
public static By SearchButton => By.Id("search_button_homepage");
}
27
Page Object Pattern
public class SearchPage
{
public const string Url = "https://duckduckgo.com/";
public static By SearchInput => By.Id("search_form_input_homepage");
public static By SearchButton => By.Id("search_button_homepage");
public IWebDriver Driver { get; private set; }
public SearchPage(IWebDriver driver) => Driver = driver;
}
28
Page Object Pattern
public class SearchPage
{
public const string Url = "https://duckduckgo.com/";
public static By SearchInput => By.Id("search_form_input_homepage");
public static By SearchButton => By.Id("search_button_homepage");
public IWebDriver Driver { get; private set; }
public SearchPage(IWebDriver driver) => Driver = driver;
public void Load() => Driver.Navigate().GoToUrl(Url);
}
29
Page Object Pattern
public class SearchPage
{
public const string Url = "https://duckduckgo.com/";
public static By SearchInput => By.Id("search_form_input_homepage");
public static By SearchButton => By.Id("search_button_homepage");
public IWebDriver Driver { get; private set; }
public SearchPage(IWebDriver driver) => Driver = driver;
public void Load() => Driver.Navigate().GoToUrl(Url);
public void Search(string phrase)
{
WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30));
wait.Until(d => d.FindElements(SearchInput).Count > 0);
Driver.FindElement(SearchInput).SendKeys(phrase);
Driver.FindElement(SearchButton).Click();
}
}
30
Page Object Pattern usage
IWebDriver driver = new ChromeDriver();
SearchPage searchPage = new SearchPage(driver);
searchPage.Load();
searchPage.Search(“panda”);
31
Page Object Pattern usage
IWebDriver driver = new ChromeDriver();
SearchPage searchPage = new SearchPage(driver);
searchPage.Load();
searchPage.Search(“panda”);
ResultPage resultPage = new ResultPage(driver);
resultPage.WaitForTitle(“panda”);
resultPage.WaitForResultLinks();
driver.Quit();
32
Page object duplication redux
public class AnyPage
{
// ...
}
33
Page object duplication redux
public class AnyPage
{
// ...
public void ClickButton()
{
Wait.Until(d => d.FindElements(Button).Count > 0);
driver.FindElement(Button).Click();
}
}
34
Page object duplication redux
public class AnyPage
{
// ...
public void ClickButton()
{
Wait.Until(d => d.FindElements(Button).Count > 0);
driver.FindElement(Button).Click();
}
public void ClickOtherButton()
{
Wait.Until(d => d.FindElements(OtherButton).Count > 0);
driver.FindElement(OtherButton).Click();
}
}
35
Page object duplication redux
public class AnyPage
{
// ...
public void ClickButton()
{
Wait.Until(d => d.FindElements(Button).Count > 0);
driver.FindElement(Button).Click();
}
public void ClickOtherButton()
{
Wait.Until(d => d.FindElements(OtherButton).Count > 0);
driver.FindElement(OtherButton).Click();
}
}
36
A base page
public class BasePage
{
}
37
A base page
public class BasePage
{
public IWebDriver Driver { get; private set; }
public WebDriverWait Wait { get; private set; }
public SearchPage(IWebDriver driver)
{
Driver = driver;
Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30));
}
}
38
A base page
public class BasePage
{
public IWebDriver Driver { get; private set; }
public WebDriverWait Wait { get; private set; }
public SearchPage(IWebDriver driver)
{
Driver = driver;
Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30));
}
protected void Click(By locator)
{
Wait.Until(d => d.FindElements(locator).Count > 0);
driver.FindElement(locator).Click();
}
}
39
Base page inheritance
public class AnyPage : BasePage
{
// ...
public AnyPage(IWebDriver driver) : base(driver) {}
public void ClickButton() => Click(Button);
public void ClickOtherButton() => Click(OtherButton);
}
40
Even more duplication?
public class AnyPage : BasePage
{
// ...
public void ClickButton() => Click(Button);
public void ClickOtherButton() => Click(OtherButton);
public void ButtonText() => Text(Button);
public void OtherButtonText() => Text(OtherButton);
public void IsButtonDisplayed() => IsDisplayed(Button);
public void IsOtherButtonDisplayed() => IsDisplayed(OtherButton);
}
Page objects
are free-form.
There’s no official version.There’s no conformity.There’s no enforcement.
Page objects are more of a “convention” than a true design pattern.
There must be
a better way.
43
Reconsidering interactions
44
Reconsidering interactions
Actor
45
Reconsidering interactions
Actor Product
46
Reconsidering interactions
Actor Interaction Product
47
Reconsidering interactions
Actor Ability Interaction Product
Actors
use Abilities
to perform Interactions.
Actors
use Abilities
to perform Interactions.
This is the heart of the Screenplay Pattern.
50
51
Boa Constrictor
• Open source C# implementation of the Screenplay Pattern
• Developed by PrecisionLender, a Q2 Company
• Can be used with any test framework (SpecFlow, NUnit, etc.)
• Doc site: q2ebanking.github.io/boa-constrictor
• GitHub repository: q2ebanking/boa-constrictor
• NuGet package: Boa.Constrictor
The .NET Screenplay Pattern
52
Let’s rewrite that
DuckDuckGo search test
using Boa Constrictor.
53
Required packages
// NuGet Packages:
// Boa.Constrictor
// FluentAssertions
// RestSharp
// Selenium.Support
// Selenium.WebDriver
using Boa.Constrictor.Logging;
using Boa.Constrictor.RestSharp;
using Boa.Constrictor.Screenplay;
using Boa.Constrictor.WebDriver;
using FluentAssertions;
using OpenQA.Selenium.Chrome;
using static Boa.Constrictor.WebDriver.WebLocator;
54
Creating the Actor
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
55
Adding Abilities
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
56
Adding Abilities
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
public class BrowseTheWeb : IAbility
{
public IWebDriver WebDriver { get; }
private BrowseTheWeb(IWebDriver driver) =>
WebDriver = driver;
public static BrowseTheWeb With(IWebDriver driver) =>
new BrowseTheWeb(driver);
}
57
Modeling web pages
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
public static class SearchPage
{
public const string Url =
"https://www.duckduckgo.com/";
public static IWebLocator SearchInput => L(
"DuckDuckGo Search Input",
By.Id("search_form_input_homepage"));
}
58
Attempting a Task
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
59
Attempting a Task
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
public void AttemptsTo(ITask task)
{
task.PerformAs(this);
}
60
Attempting a Task
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
public class Navigate : ITask
{
private string Url { get; set; }
private Navigate(string url) => Url = url;
public static Navigate ToUrl(string url) => new Navigate(url);
public void PerformAs(IActor actor)
{
var driver = actor.Using<BrowseTheWeb>().WebDriver;
driver.Navigate().GoToUrl(Url);
}
}
61
Attempting a Task
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
public class Navigate : ITask
{
private string Url { get; set; }
private Navigate(string url) => Url = url;
public static Navigate ToUrl(string url) => new Navigate(url);
public void PerformAs(IActor actor)
{
var driver = actor.Using<BrowseTheWeb>().WebDriver;
driver.Navigate().GoToUrl(Url);
}
}
62
Attempting a Task
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
public static class SearchPage
{
public const string Url =
"https://www.duckduckgo.com/";
public static IWebLocator SearchInput => L(
"DuckDuckGo Search Input",
By.Id("search_form_input_homepage"));
}
63
Asking a Question
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
64
Asking a Question
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
public TAnswer
AskingFor<TAnswer>(IQuestion<TAnswer> question)
{
return question.RequestAs(this);
}
65
Asking a Question
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
public class ValueAttribute : IQuestion<string>
{
public IWebLocator Locator { get; }
private ValueAttribute(IWebLocator locator) => Locator = locator;
public static ValueAttribute Of(IWebLocator locator) => new ValueAttribute(locator);
public string RequestAs(IActor actor)
{
var driver = actor.Using<BrowseTheWeb>().WebDriver;
actor.AttemptsTo(Wait.Until(Existence.Of(Locator), IsEqualTo.True()));
return driver.FindElement(Locator.Query).GetAttribute("value");
}
}
66
Asking a Question
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
public class ValueAttribute : IQuestion<string>
{
public IWebLocator Locator { get; }
private ValueAttribute(IWebLocator locator) => Locator = locator;
public static ValueAttribute Of(IWebLocator locator) => new ValueAttribute(locator);
public string RequestAs(IActor actor)
{
var driver = actor.Using<BrowseTheWeb>().WebDriver;
actor.AttemptsTo(Wait.Until(Existence.Of(Locator), IsEqualTo.True()));
return driver.FindElement(Locator.Query).GetAttribute("value");
}
}
67
Asking a Question
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
public static class SearchPage
{
public const string Url =
"https://www.duckduckgo.com/";
public static IWebLocator SearchInput => L(
"DuckDuckGo Search Input",
By.Id("search_form_input_homepage"));
}
68
Asking a Question
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
69
Composing a custom Interaction
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
70
Composing a custom Interaction
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
public class SearchDuckDuckGo : ITask
{
public string Phrase { get; }
private SearchDuckDuckGo(string phrase) =>
Phrase = phrase;
public static SearchDuckDuckGo For(string phrase) =>
new SearchDuckDuckGo(phrase);
public void PerformAs(IActor actor)
{
actor.AttemptsTo(SendKeys.To(SearchPage.SearchInput, Phrase));
actor.AttemptsTo(Click.On(SearchPage.SearchButton));
}
}
71
Composing a custom Interaction
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
public class SearchDuckDuckGo : ITask
{
public string Phrase { get; }
private SearchDuckDuckGo(string phrase) =>
Phrase = phrase;
public static SearchDuckDuckGo For(string phrase) =>
new SearchDuckDuckGo(phrase);
public void PerformAs(IActor actor)
{
actor.AttemptsTo(SendKeys.To(SearchPage.SearchInput, Phrase));
actor.AttemptsTo(Click.On(SearchPage.SearchButton));
}
}
72
Composing a custom Interaction
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
73
Waiting for Questions to yield answers
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
74
Waiting for Questions to yield answers
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
75
Waiting for Questions to yield answers
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
public static IWebLocator ResultLinks => L(
"DuckDuckGo Result Page Links",
By.ClassName("result__a"));
76
Waiting for Questions to yield answers
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
77
Waiting for Questions to yield answers
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
78
Quitting the web browser
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
actor.AttemptsTo(QuitWebDriver.ForBrowser());
79
A completed test
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
actor.Can(BrowseTheWeb.With(new ChromeDriver()));
actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
actor.AttemptsTo(QuitWebDriver.ForBrowser());
80
A REST API test
81
A REST API test
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
82
A REST API test
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo")));
83
A REST API test
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo")));
var request = new RestRequest("api/breeds/image/random", Method.GET);
84
A REST API test
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo")));
var request = new RestRequest("api/breeds/image/random", Method.GET);
var response = Actor.Calls(Rest.Request(request));
85
A REST API test
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo")));
var request = new RestRequest("api/breeds/image/random", Method.GET);
var response = Actor.Calls(Rest.Request(request));
response.StatusCode.Should().Be(HttpStatusCode.OK);
86
A REST API test
IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo")));
var request = new RestRequest("api/breeds/image/random", Method.GET);
var response = Actor.Calls(Rest.Request(request));
response.StatusCode.Should().Be(HttpStatusCode.OK);
Actors
Actors
use Abilities
Actors
use Abilities
to perform Interactions.
91
SOLID principles
Single-Responsibility Principle Actors,Abilities, and Interactions are treated as separate
concerns.
Open-Closed Principle Each new Interaction must be a new class, rather than a
modification of an existing class.
Liskov Substitution Principle Actors can call all Abilities and Interactions the same way.
Interface Segregation Principle Actors,Abilities, and Interactions each have distinct,
separate interfaces.
Dependency Inversion Principle Abilities and Interactions are handled as interfaces.
Interactions use Abilities via dependency injection from
the Actor.
92
Why use Screenplay?
93
Why use Screenplay?
1. Rich, reusable, reliable interactions
94
Why use Screenplay?
1. Rich, reusable, reliable interactions
2. Interactions are composable
95
Why use Screenplay?
1. Rich, reusable, reliable interactions
2. Interactions are composable
3. Easy waiting
96
Why use Screenplay?
1. Rich, reusable, reliable interactions
2. Interactions are composable
3. Easy waiting
4. Readable, understandable calls
97
Why use Screenplay?
1. Rich, reusable, reliable interactions
2. Interactions are composable
3. Easy waiting
4. Readable, understandable calls
5. Covers all interactions – not justWeb UI
98
The Screenplay Pattern
99
The Screenplay Pattern
provides better interactions
100
The Screenplay Pattern
provides better interactions
for better automation.
101
Actors use Abilities to perform Interactions
Actor Ability Interaction
102
Getting started
103
Getting started
104
Getting started
105
Getting started
106
Getting started
107
https://q2ebanking.github.io/boa-constrictor/
108
109
I’ll mail Boa Constrictor stickers to 10 people who
tweet what they learned from this webinar!
Include @AutomationPanda & #BoaConstrictor!

More Related Content

What's hot

Demystifying Angular Animations
Demystifying Angular AnimationsDemystifying Angular Animations
Demystifying Angular Animations
Gil Fink
 
Easy tests with Selenide and Easyb
Easy tests with Selenide and EasybEasy tests with Selenide and Easyb
Easy tests with Selenide and Easyb
Iakiv Kramarenko
 
우아한 모노리스
우아한 모노리스우아한 모노리스
우아한 모노리스
Arawn Park
 
Cypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdfCypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdf
bacancytechnology
 
Page Object Model and Implementation in Selenium
Page Object Model and Implementation in Selenium  Page Object Model and Implementation in Selenium
Page Object Model and Implementation in Selenium
Zoe Gilbert
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
beom kyun choi
 
Graphs in the Database: Rdbms In The Social Networks Age
Graphs in the Database: Rdbms In The Social Networks AgeGraphs in the Database: Rdbms In The Social Networks Age
Graphs in the Database: Rdbms In The Social Networks Age
Lorenzo Alberton
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
Dmitry Buzdin
 
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшeQA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
QAFest
 
Selenium cheat sheet
Selenium cheat sheetSelenium cheat sheet
Selenium cheat sheet
Sri Priya P Kulkarni
 
Microservices Technology Stack
Microservices Technology StackMicroservices Technology Stack
Microservices Technology Stack
Eberhard Wolff
 
nginx 입문 공부자료
nginx 입문 공부자료nginx 입문 공부자료
nginx 입문 공부자료
choi sungwook
 
Automation testing on ios platform using appium
Automation testing on ios platform using appiumAutomation testing on ios platform using appium
Automation testing on ios platform using appium
Ambreen Khan
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
SangIn Choung
 
Selenium web driver
Selenium web driverSelenium web driver
Selenium web driver
Roman Savitskiy
 
Selenium interview questions and answers
Selenium interview questions and answersSelenium interview questions and answers
Selenium interview questions and answers
kavinilavuG
 
Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...
Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...
Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...
Edureka!
 
Automated-Testing-inside-containers
Automated-Testing-inside-containersAutomated-Testing-inside-containers
Automated-Testing-inside-containers
Manoj Kumar Kumar
 
Top 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdetTop 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdet
DevLabs Alliance
 
Page object pattern
Page object patternPage object pattern
Page object pattern
Petro Konopelko
 

What's hot (20)

Demystifying Angular Animations
Demystifying Angular AnimationsDemystifying Angular Animations
Demystifying Angular Animations
 
Easy tests with Selenide and Easyb
Easy tests with Selenide and EasybEasy tests with Selenide and Easyb
Easy tests with Selenide and Easyb
 
우아한 모노리스
우아한 모노리스우아한 모노리스
우아한 모노리스
 
Cypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdfCypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdf
 
Page Object Model and Implementation in Selenium
Page Object Model and Implementation in Selenium  Page Object Model and Implementation in Selenium
Page Object Model and Implementation in Selenium
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
 
Graphs in the Database: Rdbms In The Social Networks Age
Graphs in the Database: Rdbms In The Social Networks AgeGraphs in the Database: Rdbms In The Social Networks Age
Graphs in the Database: Rdbms In The Social Networks Age
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшeQA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
 
Selenium cheat sheet
Selenium cheat sheetSelenium cheat sheet
Selenium cheat sheet
 
Microservices Technology Stack
Microservices Technology StackMicroservices Technology Stack
Microservices Technology Stack
 
nginx 입문 공부자료
nginx 입문 공부자료nginx 입문 공부자료
nginx 입문 공부자료
 
Automation testing on ios platform using appium
Automation testing on ios platform using appiumAutomation testing on ios platform using appium
Automation testing on ios platform using appium
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
 
Selenium web driver
Selenium web driverSelenium web driver
Selenium web driver
 
Selenium interview questions and answers
Selenium interview questions and answersSelenium interview questions and answers
Selenium interview questions and answers
 
Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...
Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...
Selenium Page Object Model Using Page Factory | Selenium Tutorial For Beginne...
 
Automated-Testing-inside-containers
Automated-Testing-inside-containersAutomated-Testing-inside-containers
Automated-Testing-inside-containers
 
Top 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdetTop 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdet
 
Page object pattern
Page object patternPage object pattern
Page object pattern
 

Similar to The Screenplay Pattern: Better Interactions for Better Automation

Session on "The Screenplay Pattern: Better Interactions for Better Automation...
Session on "The Screenplay Pattern: Better Interactions for Better Automation...Session on "The Screenplay Pattern: Better Interactions for Better Automation...
Session on "The Screenplay Pattern: Better Interactions for Better Automation...
Agile Testing Alliance
 
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingBDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
John Ferguson Smart Limited
 
Google
GoogleGoogle
Google
soon
 
GDG İstanbul Şubat Etkinliği - Sunum
GDG İstanbul Şubat Etkinliği - SunumGDG İstanbul Şubat Etkinliği - Sunum
GDG İstanbul Şubat Etkinliği - Sunum
Cüneyt Yeşilkaya
 
Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)
Python Ireland
 
Continuous integration using thucydides(bdd) with selenium
Continuous integration using thucydides(bdd) with seleniumContinuous integration using thucydides(bdd) with selenium
Continuous integration using thucydides(bdd) with selenium
Xebia IT Architects
 
Test automation
Test  automationTest  automation
Test automation
Kaushik Banerjee
 
Experienced Selenium Interview questions
Experienced Selenium Interview questionsExperienced Selenium Interview questions
Experienced Selenium Interview questions
archana singh
 
Behavior-Driven Development and Automation Testing Using Cucumber Framework W...
Behavior-Driven Development and Automation Testing Using Cucumber Framework W...Behavior-Driven Development and Automation Testing Using Cucumber Framework W...
Behavior-Driven Development and Automation Testing Using Cucumber Framework W...
KMS Technology
 
Continuous integration using thucydides(bdd) with selenium
Continuous integration using thucydides(bdd) with  seleniumContinuous integration using thucydides(bdd) with  selenium
Continuous integration using thucydides(bdd) with selenium
Khyati Sehgal
 
The Ring programming language version 1.2 book - Part 31 of 84
The Ring programming language version 1.2 book - Part 31 of 84The Ring programming language version 1.2 book - Part 31 of 84
The Ring programming language version 1.2 book - Part 31 of 84
Mahmoud Samir Fayed
 
How to execute Automation Testing using Selenium
How to execute Automation Testing using SeleniumHow to execute Automation Testing using Selenium
How to execute Automation Testing using Selenium
valuebound
 
Writing automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjectsWriting automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjects
Leticia Rss
 
Taiko - Reliable Browser Automation
Taiko - Reliable Browser AutomationTaiko - Reliable Browser Automation
Taiko - Reliable Browser Automation
Harmeet Singh
 
Demystifying Keyword Driven Using Watir
Demystifying Keyword Driven Using WatirDemystifying Keyword Driven Using Watir
Demystifying Keyword Driven Using Watir
Hirday Lamba
 
Complete_QA_Automation_Guide__1696637878.pdf
Complete_QA_Automation_Guide__1696637878.pdfComplete_QA_Automation_Guide__1696637878.pdf
Complete_QA_Automation_Guide__1696637878.pdf
ramya9288
 
Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015
Naresha K
 
Testing web application with Python
Testing web application with PythonTesting web application with Python
Testing web application with Python
Jachym Cepicky
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideNode.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java side
Mek Srunyu Stittri
 
Testing ASP.NET - Progressive.NET
Testing ASP.NET - Progressive.NETTesting ASP.NET - Progressive.NET
Testing ASP.NET - Progressive.NET
Ben Hall
 

Similar to The Screenplay Pattern: Better Interactions for Better Automation (20)

Session on "The Screenplay Pattern: Better Interactions for Better Automation...
Session on "The Screenplay Pattern: Better Interactions for Better Automation...Session on "The Screenplay Pattern: Better Interactions for Better Automation...
Session on "The Screenplay Pattern: Better Interactions for Better Automation...
 
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingBDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
 
Google
GoogleGoogle
Google
 
GDG İstanbul Şubat Etkinliği - Sunum
GDG İstanbul Şubat Etkinliği - SunumGDG İstanbul Şubat Etkinliği - Sunum
GDG İstanbul Şubat Etkinliği - Sunum
 
Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)
 
Continuous integration using thucydides(bdd) with selenium
Continuous integration using thucydides(bdd) with seleniumContinuous integration using thucydides(bdd) with selenium
Continuous integration using thucydides(bdd) with selenium
 
Test automation
Test  automationTest  automation
Test automation
 
Experienced Selenium Interview questions
Experienced Selenium Interview questionsExperienced Selenium Interview questions
Experienced Selenium Interview questions
 
Behavior-Driven Development and Automation Testing Using Cucumber Framework W...
Behavior-Driven Development and Automation Testing Using Cucumber Framework W...Behavior-Driven Development and Automation Testing Using Cucumber Framework W...
Behavior-Driven Development and Automation Testing Using Cucumber Framework W...
 
Continuous integration using thucydides(bdd) with selenium
Continuous integration using thucydides(bdd) with  seleniumContinuous integration using thucydides(bdd) with  selenium
Continuous integration using thucydides(bdd) with selenium
 
The Ring programming language version 1.2 book - Part 31 of 84
The Ring programming language version 1.2 book - Part 31 of 84The Ring programming language version 1.2 book - Part 31 of 84
The Ring programming language version 1.2 book - Part 31 of 84
 
How to execute Automation Testing using Selenium
How to execute Automation Testing using SeleniumHow to execute Automation Testing using Selenium
How to execute Automation Testing using Selenium
 
Writing automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjectsWriting automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjects
 
Taiko - Reliable Browser Automation
Taiko - Reliable Browser AutomationTaiko - Reliable Browser Automation
Taiko - Reliable Browser Automation
 
Demystifying Keyword Driven Using Watir
Demystifying Keyword Driven Using WatirDemystifying Keyword Driven Using Watir
Demystifying Keyword Driven Using Watir
 
Complete_QA_Automation_Guide__1696637878.pdf
Complete_QA_Automation_Guide__1696637878.pdfComplete_QA_Automation_Guide__1696637878.pdf
Complete_QA_Automation_Guide__1696637878.pdf
 
Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015
 
Testing web application with Python
Testing web application with PythonTesting web application with Python
Testing web application with Python
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideNode.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java side
 
Testing ASP.NET - Progressive.NET
Testing ASP.NET - Progressive.NETTesting ASP.NET - Progressive.NET
Testing ASP.NET - Progressive.NET
 

More from Applitools

Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Applitools
 
Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...
Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...
Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...
Applitools
 
Visual AI for eCommerce: Improving Conversions with a Flawless UI
Visual AI for eCommerce: Improving Conversions with a Flawless UIVisual AI for eCommerce: Improving Conversions with a Flawless UI
Visual AI for eCommerce: Improving Conversions with a Flawless UI
Applitools
 
A Test Automation Platform Designed for the Future
A Test Automation Platform Designed for the FutureA Test Automation Platform Designed for the Future
A Test Automation Platform Designed for the Future
Applitools
 
Add AI to Your SDLC, presented by Applitools and Curiosity
Add AI to Your SDLC, presented by Applitools and CuriosityAdd AI to Your SDLC, presented by Applitools and Curiosity
Add AI to Your SDLC, presented by Applitools and Curiosity
Applitools
 
The Future of AI-Based Test Automation
The Future of AI-Based Test AutomationThe Future of AI-Based Test Automation
The Future of AI-Based Test Automation
Applitools
 
Test Automation at Scale: Lessons from Top-Performing Distributed Teams
Test Automation at Scale: Lessons from Top-Performing Distributed TeamsTest Automation at Scale: Lessons from Top-Performing Distributed Teams
Test Automation at Scale: Lessons from Top-Performing Distributed Teams
Applitools
 
Can AI Autogenerate and Run Automated Tests?
Can AI Autogenerate and Run Automated Tests?Can AI Autogenerate and Run Automated Tests?
Can AI Autogenerate and Run Automated Tests?
Applitools
 
Triple Assurance: AI-Powered Test Automation in UI Design and Functionality
Triple Assurance: AI-Powered Test Automation in UI Design and FunctionalityTriple Assurance: AI-Powered Test Automation in UI Design and Functionality
Triple Assurance: AI-Powered Test Automation in UI Design and Functionality
Applitools
 
Navigating the Challenges of Testing at Scale: Lessons from Top-Performing Teams
Navigating the Challenges of Testing at Scale: Lessons from Top-Performing TeamsNavigating the Challenges of Testing at Scale: Lessons from Top-Performing Teams
Navigating the Challenges of Testing at Scale: Lessons from Top-Performing Teams
Applitools
 
Introducing the Applitools Self Healing Execution Cloud.pdf
Introducing the Applitools Self Healing Execution Cloud.pdfIntroducing the Applitools Self Healing Execution Cloud.pdf
Introducing the Applitools Self Healing Execution Cloud.pdf
Applitools
 
Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...
Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...
Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...
Applitools
 
Collaborating From Design To Experience: Introducing Centra
Collaborating From Design To Experience: Introducing CentraCollaborating From Design To Experience: Introducing Centra
Collaborating From Design To Experience: Introducing Centra
Applitools
 
What the QA Position Will Look Like in the Future
What the QA Position Will Look Like in the FutureWhat the QA Position Will Look Like in the Future
What the QA Position Will Look Like in the Future
Applitools
 
Getting Started with Visual Testing
Getting Started with Visual TestingGetting Started with Visual Testing
Getting Started with Visual Testing
Applitools
 
Workshop: Head-to-Head Web Testing: Part 1 with Cypress
Workshop: Head-to-Head Web Testing: Part 1 with CypressWorkshop: Head-to-Head Web Testing: Part 1 with Cypress
Workshop: Head-to-Head Web Testing: Part 1 with Cypress
Applitools
 
From Washing Cars To Automating Test Applications
From Washing Cars To Automating Test ApplicationsFrom Washing Cars To Automating Test Applications
From Washing Cars To Automating Test Applications
Applitools
 
A Holistic Approach to Testing in Continuous Delivery
A Holistic Approach to Testing in Continuous DeliveryA Holistic Approach to Testing in Continuous Delivery
A Holistic Approach to Testing in Continuous Delivery
Applitools
 
AI-Powered-Cross-Browser Testing
AI-Powered-Cross-Browser TestingAI-Powered-Cross-Browser Testing
AI-Powered-Cross-Browser Testing
Applitools
 
Workshop: An Introduction to API Automation with Javascript
Workshop: An Introduction to API Automation with JavascriptWorkshop: An Introduction to API Automation with Javascript
Workshop: An Introduction to API Automation with Javascript
Applitools
 

More from Applitools (20)

Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
 
Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...
Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...
Streamlining Your Tech Stack: A Blueprint for Enhanced Efficiency and Coverag...
 
Visual AI for eCommerce: Improving Conversions with a Flawless UI
Visual AI for eCommerce: Improving Conversions with a Flawless UIVisual AI for eCommerce: Improving Conversions with a Flawless UI
Visual AI for eCommerce: Improving Conversions with a Flawless UI
 
A Test Automation Platform Designed for the Future
A Test Automation Platform Designed for the FutureA Test Automation Platform Designed for the Future
A Test Automation Platform Designed for the Future
 
Add AI to Your SDLC, presented by Applitools and Curiosity
Add AI to Your SDLC, presented by Applitools and CuriosityAdd AI to Your SDLC, presented by Applitools and Curiosity
Add AI to Your SDLC, presented by Applitools and Curiosity
 
The Future of AI-Based Test Automation
The Future of AI-Based Test AutomationThe Future of AI-Based Test Automation
The Future of AI-Based Test Automation
 
Test Automation at Scale: Lessons from Top-Performing Distributed Teams
Test Automation at Scale: Lessons from Top-Performing Distributed TeamsTest Automation at Scale: Lessons from Top-Performing Distributed Teams
Test Automation at Scale: Lessons from Top-Performing Distributed Teams
 
Can AI Autogenerate and Run Automated Tests?
Can AI Autogenerate and Run Automated Tests?Can AI Autogenerate and Run Automated Tests?
Can AI Autogenerate and Run Automated Tests?
 
Triple Assurance: AI-Powered Test Automation in UI Design and Functionality
Triple Assurance: AI-Powered Test Automation in UI Design and FunctionalityTriple Assurance: AI-Powered Test Automation in UI Design and Functionality
Triple Assurance: AI-Powered Test Automation in UI Design and Functionality
 
Navigating the Challenges of Testing at Scale: Lessons from Top-Performing Teams
Navigating the Challenges of Testing at Scale: Lessons from Top-Performing TeamsNavigating the Challenges of Testing at Scale: Lessons from Top-Performing Teams
Navigating the Challenges of Testing at Scale: Lessons from Top-Performing Teams
 
Introducing the Applitools Self Healing Execution Cloud.pdf
Introducing the Applitools Self Healing Execution Cloud.pdfIntroducing the Applitools Self Healing Execution Cloud.pdf
Introducing the Applitools Self Healing Execution Cloud.pdf
 
Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...
Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...
Unlocking the Power of ChatGPT and AI in Testing - NextSteps, presented by Ap...
 
Collaborating From Design To Experience: Introducing Centra
Collaborating From Design To Experience: Introducing CentraCollaborating From Design To Experience: Introducing Centra
Collaborating From Design To Experience: Introducing Centra
 
What the QA Position Will Look Like in the Future
What the QA Position Will Look Like in the FutureWhat the QA Position Will Look Like in the Future
What the QA Position Will Look Like in the Future
 
Getting Started with Visual Testing
Getting Started with Visual TestingGetting Started with Visual Testing
Getting Started with Visual Testing
 
Workshop: Head-to-Head Web Testing: Part 1 with Cypress
Workshop: Head-to-Head Web Testing: Part 1 with CypressWorkshop: Head-to-Head Web Testing: Part 1 with Cypress
Workshop: Head-to-Head Web Testing: Part 1 with Cypress
 
From Washing Cars To Automating Test Applications
From Washing Cars To Automating Test ApplicationsFrom Washing Cars To Automating Test Applications
From Washing Cars To Automating Test Applications
 
A Holistic Approach to Testing in Continuous Delivery
A Holistic Approach to Testing in Continuous DeliveryA Holistic Approach to Testing in Continuous Delivery
A Holistic Approach to Testing in Continuous Delivery
 
AI-Powered-Cross-Browser Testing
AI-Powered-Cross-Browser TestingAI-Powered-Cross-Browser Testing
AI-Powered-Cross-Browser Testing
 
Workshop: An Introduction to API Automation with Javascript
Workshop: An Introduction to API Automation with JavascriptWorkshop: An Introduction to API Automation with Javascript
Workshop: An Introduction to API Automation with Javascript
 

Recently uploaded

Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative AnalysisOdoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Envertis Software Solutions
 
What next after learning python programming basics
What next after learning python programming basicsWhat next after learning python programming basics
What next after learning python programming basics
Rakesh Kumar R
 
How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
Rakesh Kumar R
 
Fundamentals of Programming and Language Processors
Fundamentals of Programming and Language ProcessorsFundamentals of Programming and Language Processors
Fundamentals of Programming and Language Processors
Rakesh Kumar R
 
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
dakas1
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
Ayan Halder
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
Hornet Dynamics
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
brainerhub1
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
mz5nrf0n
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
Patrick Weigel
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
SOCRadar
 
Top 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptxTop 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptx
devvsandy
 
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdfTop Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
VALiNTRY360
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
dakas1
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
Yara Milbes
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
Green Software Development
 
Malibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed RoundMalibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed Round
sjcobrien
 
Mobile app Development Services | Drona Infotech
Mobile app Development Services  | Drona InfotechMobile app Development Services  | Drona Infotech
Mobile app Development Services | Drona Infotech
Drona Infotech
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
Marcin Chrost
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 

Recently uploaded (20)

Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative AnalysisOdoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
 
What next after learning python programming basics
What next after learning python programming basicsWhat next after learning python programming basics
What next after learning python programming basics
 
How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
 
Fundamentals of Programming and Language Processors
Fundamentals of Programming and Language ProcessorsFundamentals of Programming and Language Processors
Fundamentals of Programming and Language Processors
 
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
 
Top 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptxTop 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptx
 
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdfTop Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
 
Malibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed RoundMalibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed Round
 
Mobile app Development Services | Drona Infotech
Mobile app Development Services  | Drona InfotechMobile app Development Services  | Drona Infotech
Mobile app Development Services | Drona Infotech
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 

The Screenplay Pattern: Better Interactions for Better Automation

  • 1. The Screenplay Pattern Better Interactions for Better Automation
  • 2. 2 Andrew “Pandy” Knight Lead Software Engineer in Test PrecisionLender, a Q2 Company AutomationPanda.com @AutomationPanda
  • 3. 3 Andrew “Pandy” Knight Lead Developer for Boa Constrictor
  • 4. 4 3-Part Talk 1. Problems with traditional interactions 2. Why Screenplay is better 3. Using Screenplay with Boa Constrictor
  • 5.
  • 7. Interactions How users operate software: loads, clicks, scrapes, etc.
  • 9. 9 A simple DuckDuckGo search test Open the search engine Search for a phrase Verify results appear
  • 10. 10 A simple DuckDuckGo search test Open the search engine Search for a phrase Verify results appear 1. Navigate
  • 11. 11 A simple DuckDuckGo search test Open the search engine Search for a phrase Verify results appear 1. Navigate 1. Enter search phrase 2. Click search button
  • 12. 12 A simple DuckDuckGo search test Open the search engine Search for a phrase Verify results appear 1. Navigate 1. Enter search phrase 2. Click search button 1. Scrape page title 2. Scrape result links
  • 13. 13
  • 15. 15 Raw WebDriver calls IWebDriver driver = new ChromeDriver();
  • 16. 16 Raw WebDriver calls IWebDriver driver = new ChromeDriver(); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/");
  • 17. 17 Raw WebDriver calls IWebDriver driver = new ChromeDriver(); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/"); // Search for a phrase driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click();
  • 18. 18 Raw WebDriver calls IWebDriver driver = new ChromeDriver(); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/"); // Search for a phrase driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click(); // Verify results appear driver.Title.ToLower().Should().Contain("panda"); driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0);
  • 19. 19 Raw WebDriver calls IWebDriver driver = new ChromeDriver(); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/"); // Search for a phrase driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click(); // Verify results appear driver.Title.ToLower().Should().Contain("panda"); driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0); driver.Quit();
  • 20. 20 Raw WebDriver calls IWebDriver driver = new ChromeDriver(); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/"); // Search for a phrase driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click(); // Verify results appear driver.Title.ToLower().Should().Contain("panda"); driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0); driver.Quit(); Race Conditions ! 1 2 3
  • 21. 21 Implicit waits IWebDriver driver = new ChromeDriver(); driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/"); // Search for a phrase driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click(); // Verify results appear driver.Title.ToLower().Should().Contain("panda"); driver.FindElements(By.CssSelector("a.result__a")).Should().BeGreaterThan(0); driver.Quit();
  • 22. 22 Explicit waits IWebDriver driver = new ChromeDriver(); WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/"); // Search for a phrase wait.Until(d => d.FindElements(By.Id("search_form_input_homepage")).Count > 0); driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click(); // Verify results appear wait.Until(d => d.Title.ToLower().Contains("panda")); wait.Until(d => d.FindElements(By.CssSelector("a.result__a"))).Count > 0); driver.Quit();
  • 23. 23 Duplicate code IWebDriver driver = new ChromeDriver(); WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)); // Open the search engine driver.Navigate().GoToUrl("https://duckduckgo.com/"); // Search for a phrase wait.Until(d => d.FindElements(By.Id("search_form_input_homepage")).Count > 0); driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click(); // Verify results appear wait.Until(d => d.Title.ToLower().Contains("panda")); wait.Until(d => d.FindElements(By.CssSelector("a.result__a"))).Count > 0); driver.Quit();
  • 24. 24 Unintuitive code IWebDriver driver = new ChromeDriver(); WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)); driver.Navigate().GoToUrl("https://duckduckgo.com/"); wait.Until(d => d.FindElements(By.Id("search_form_input_homepage")).Count > 0); driver.FindElement(By.Id("search_form_input_homepage")).SendKeys("panda"); driver.FindElement(By.Id("search_button_homepage")).Click(); wait.Until(d => d.Title.ToLower().Contains("panda")); wait.Until(d => d.FindElements(By.CssSelector("a.result__a"))).Count > 0); driver.Quit();
  • 25. 25 Page Object Pattern public class SearchPage { }
  • 26. 26 Page Object Pattern public class SearchPage { public const string Url = "https://duckduckgo.com/"; public static By SearchInput => By.Id("search_form_input_homepage"); public static By SearchButton => By.Id("search_button_homepage"); }
  • 27. 27 Page Object Pattern public class SearchPage { public const string Url = "https://duckduckgo.com/"; public static By SearchInput => By.Id("search_form_input_homepage"); public static By SearchButton => By.Id("search_button_homepage"); public IWebDriver Driver { get; private set; } public SearchPage(IWebDriver driver) => Driver = driver; }
  • 28. 28 Page Object Pattern public class SearchPage { public const string Url = "https://duckduckgo.com/"; public static By SearchInput => By.Id("search_form_input_homepage"); public static By SearchButton => By.Id("search_button_homepage"); public IWebDriver Driver { get; private set; } public SearchPage(IWebDriver driver) => Driver = driver; public void Load() => Driver.Navigate().GoToUrl(Url); }
  • 29. 29 Page Object Pattern public class SearchPage { public const string Url = "https://duckduckgo.com/"; public static By SearchInput => By.Id("search_form_input_homepage"); public static By SearchButton => By.Id("search_button_homepage"); public IWebDriver Driver { get; private set; } public SearchPage(IWebDriver driver) => Driver = driver; public void Load() => Driver.Navigate().GoToUrl(Url); public void Search(string phrase) { WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30)); wait.Until(d => d.FindElements(SearchInput).Count > 0); Driver.FindElement(SearchInput).SendKeys(phrase); Driver.FindElement(SearchButton).Click(); } }
  • 30. 30 Page Object Pattern usage IWebDriver driver = new ChromeDriver(); SearchPage searchPage = new SearchPage(driver); searchPage.Load(); searchPage.Search(“panda”);
  • 31. 31 Page Object Pattern usage IWebDriver driver = new ChromeDriver(); SearchPage searchPage = new SearchPage(driver); searchPage.Load(); searchPage.Search(“panda”); ResultPage resultPage = new ResultPage(driver); resultPage.WaitForTitle(“panda”); resultPage.WaitForResultLinks(); driver.Quit();
  • 32. 32 Page object duplication redux public class AnyPage { // ... }
  • 33. 33 Page object duplication redux public class AnyPage { // ... public void ClickButton() { Wait.Until(d => d.FindElements(Button).Count > 0); driver.FindElement(Button).Click(); } }
  • 34. 34 Page object duplication redux public class AnyPage { // ... public void ClickButton() { Wait.Until(d => d.FindElements(Button).Count > 0); driver.FindElement(Button).Click(); } public void ClickOtherButton() { Wait.Until(d => d.FindElements(OtherButton).Count > 0); driver.FindElement(OtherButton).Click(); } }
  • 35. 35 Page object duplication redux public class AnyPage { // ... public void ClickButton() { Wait.Until(d => d.FindElements(Button).Count > 0); driver.FindElement(Button).Click(); } public void ClickOtherButton() { Wait.Until(d => d.FindElements(OtherButton).Count > 0); driver.FindElement(OtherButton).Click(); } }
  • 36. 36 A base page public class BasePage { }
  • 37. 37 A base page public class BasePage { public IWebDriver Driver { get; private set; } public WebDriverWait Wait { get; private set; } public SearchPage(IWebDriver driver) { Driver = driver; Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30)); } }
  • 38. 38 A base page public class BasePage { public IWebDriver Driver { get; private set; } public WebDriverWait Wait { get; private set; } public SearchPage(IWebDriver driver) { Driver = driver; Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30)); } protected void Click(By locator) { Wait.Until(d => d.FindElements(locator).Count > 0); driver.FindElement(locator).Click(); } }
  • 39. 39 Base page inheritance public class AnyPage : BasePage { // ... public AnyPage(IWebDriver driver) : base(driver) {} public void ClickButton() => Click(Button); public void ClickOtherButton() => Click(OtherButton); }
  • 40. 40 Even more duplication? public class AnyPage : BasePage { // ... public void ClickButton() => Click(Button); public void ClickOtherButton() => Click(OtherButton); public void ButtonText() => Text(Button); public void OtherButtonText() => Text(OtherButton); public void IsButtonDisplayed() => IsDisplayed(Button); public void IsOtherButtonDisplayed() => IsDisplayed(OtherButton); }
  • 41. Page objects are free-form. There’s no official version.There’s no conformity.There’s no enforcement. Page objects are more of a “convention” than a true design pattern.
  • 42. There must be a better way.
  • 49. Actors use Abilities to perform Interactions. This is the heart of the Screenplay Pattern.
  • 50. 50
  • 51. 51 Boa Constrictor • Open source C# implementation of the Screenplay Pattern • Developed by PrecisionLender, a Q2 Company • Can be used with any test framework (SpecFlow, NUnit, etc.) • Doc site: q2ebanking.github.io/boa-constrictor • GitHub repository: q2ebanking/boa-constrictor • NuGet package: Boa.Constrictor The .NET Screenplay Pattern
  • 52. 52 Let’s rewrite that DuckDuckGo search test using Boa Constrictor.
  • 53. 53 Required packages // NuGet Packages: // Boa.Constrictor // FluentAssertions // RestSharp // Selenium.Support // Selenium.WebDriver using Boa.Constrictor.Logging; using Boa.Constrictor.RestSharp; using Boa.Constrictor.Screenplay; using Boa.Constrictor.WebDriver; using FluentAssertions; using OpenQA.Selenium.Chrome; using static Boa.Constrictor.WebDriver.WebLocator;
  • 54. 54 Creating the Actor IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
  • 55. 55 Adding Abilities IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver()));
  • 56. 56 Adding Abilities IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); public class BrowseTheWeb : IAbility { public IWebDriver WebDriver { get; } private BrowseTheWeb(IWebDriver driver) => WebDriver = driver; public static BrowseTheWeb With(IWebDriver driver) => new BrowseTheWeb(driver); }
  • 57. 57 Modeling web pages IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); public static class SearchPage { public const string Url = "https://www.duckduckgo.com/"; public static IWebLocator SearchInput => L( "DuckDuckGo Search Input", By.Id("search_form_input_homepage")); }
  • 58. 58 Attempting a Task IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url));
  • 59. 59 Attempting a Task IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); public void AttemptsTo(ITask task) { task.PerformAs(this); }
  • 60. 60 Attempting a Task IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); public class Navigate : ITask { private string Url { get; set; } private Navigate(string url) => Url = url; public static Navigate ToUrl(string url) => new Navigate(url); public void PerformAs(IActor actor) { var driver = actor.Using<BrowseTheWeb>().WebDriver; driver.Navigate().GoToUrl(Url); } }
  • 61. 61 Attempting a Task IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); public class Navigate : ITask { private string Url { get; set; } private Navigate(string url) => Url = url; public static Navigate ToUrl(string url) => new Navigate(url); public void PerformAs(IActor actor) { var driver = actor.Using<BrowseTheWeb>().WebDriver; driver.Navigate().GoToUrl(Url); } }
  • 62. 62 Attempting a Task IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); public static class SearchPage { public const string Url = "https://www.duckduckgo.com/"; public static IWebLocator SearchInput => L( "DuckDuckGo Search Input", By.Id("search_form_input_homepage")); }
  • 63. 63 Asking a Question IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
  • 64. 64 Asking a Question IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); public TAnswer AskingFor<TAnswer>(IQuestion<TAnswer> question) { return question.RequestAs(this); }
  • 65. 65 Asking a Question IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); public class ValueAttribute : IQuestion<string> { public IWebLocator Locator { get; } private ValueAttribute(IWebLocator locator) => Locator = locator; public static ValueAttribute Of(IWebLocator locator) => new ValueAttribute(locator); public string RequestAs(IActor actor) { var driver = actor.Using<BrowseTheWeb>().WebDriver; actor.AttemptsTo(Wait.Until(Existence.Of(Locator), IsEqualTo.True())); return driver.FindElement(Locator.Query).GetAttribute("value"); } }
  • 66. 66 Asking a Question IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); public class ValueAttribute : IQuestion<string> { public IWebLocator Locator { get; } private ValueAttribute(IWebLocator locator) => Locator = locator; public static ValueAttribute Of(IWebLocator locator) => new ValueAttribute(locator); public string RequestAs(IActor actor) { var driver = actor.Using<BrowseTheWeb>().WebDriver; actor.AttemptsTo(Wait.Until(Existence.Of(Locator), IsEqualTo.True())); return driver.FindElement(Locator.Query).GetAttribute("value"); } }
  • 67. 67 Asking a Question IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); public static class SearchPage { public const string Url = "https://www.duckduckgo.com/"; public static IWebLocator SearchInput => L( "DuckDuckGo Search Input", By.Id("search_form_input_homepage")); }
  • 68. 68 Asking a Question IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty();
  • 69. 69 Composing a custom Interaction IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
  • 70. 70 Composing a custom Interaction IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); public class SearchDuckDuckGo : ITask { public string Phrase { get; } private SearchDuckDuckGo(string phrase) => Phrase = phrase; public static SearchDuckDuckGo For(string phrase) => new SearchDuckDuckGo(phrase); public void PerformAs(IActor actor) { actor.AttemptsTo(SendKeys.To(SearchPage.SearchInput, Phrase)); actor.AttemptsTo(Click.On(SearchPage.SearchButton)); } }
  • 71. 71 Composing a custom Interaction IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); public class SearchDuckDuckGo : ITask { public string Phrase { get; } private SearchDuckDuckGo(string phrase) => Phrase = phrase; public static SearchDuckDuckGo For(string phrase) => new SearchDuckDuckGo(phrase); public void PerformAs(IActor actor) { actor.AttemptsTo(SendKeys.To(SearchPage.SearchInput, Phrase)); actor.AttemptsTo(Click.On(SearchPage.SearchButton)); } }
  • 72. 72 Composing a custom Interaction IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda"));
  • 73. 73 Waiting for Questions to yield answers IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
  • 74. 74 Waiting for Questions to yield answers IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
  • 75. 75 Waiting for Questions to yield answers IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True()); public static IWebLocator ResultLinks => L( "DuckDuckGo Result Page Links", By.ClassName("result__a"));
  • 76. 76 Waiting for Questions to yield answers IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
  • 77. 77 Waiting for Questions to yield answers IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True());
  • 78. 78 Quitting the web browser IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True()); actor.AttemptsTo(QuitWebDriver.ForBrowser());
  • 79. 79 A completed test IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); actor.Can(BrowseTheWeb.With(new ChromeDriver())); actor.AttemptsTo(Navigate.ToUrl(SearchPage.Url)); actor.AskingFor(ValueAttribute.Of(SearchPage.SearchInput)).Should().BeEmpty(); actor.AttemptsTo(SearchDuckDuckGo.For("panda")); actor.WaitsUntil(Appearance.Of(ResultPage.ResultLinks), IsEqualTo.True()); actor.AttemptsTo(QuitWebDriver.ForBrowser());
  • 81. 81 A REST API test IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger());
  • 82. 82 A REST API test IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo")));
  • 83. 83 A REST API test IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo"))); var request = new RestRequest("api/breeds/image/random", Method.GET);
  • 84. 84 A REST API test IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo"))); var request = new RestRequest("api/breeds/image/random", Method.GET); var response = Actor.Calls(Rest.Request(request));
  • 85. 85 A REST API test IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo"))); var request = new RestRequest("api/breeds/image/random", Method.GET); var response = Actor.Calls(Rest.Request(request)); response.StatusCode.Should().Be(HttpStatusCode.OK);
  • 86. 86 A REST API test IActor actor = new Actor(name: "Andy", logger: new ConsoleLogger()); Actor.Can(CallRestApi.Using(new RestClient("https://dog.ceo"))); var request = new RestRequest("api/breeds/image/random", Method.GET); var response = Actor.Calls(Rest.Request(request)); response.StatusCode.Should().Be(HttpStatusCode.OK);
  • 87.
  • 91. 91 SOLID principles Single-Responsibility Principle Actors,Abilities, and Interactions are treated as separate concerns. Open-Closed Principle Each new Interaction must be a new class, rather than a modification of an existing class. Liskov Substitution Principle Actors can call all Abilities and Interactions the same way. Interface Segregation Principle Actors,Abilities, and Interactions each have distinct, separate interfaces. Dependency Inversion Principle Abilities and Interactions are handled as interfaces. Interactions use Abilities via dependency injection from the Actor.
  • 93. 93 Why use Screenplay? 1. Rich, reusable, reliable interactions
  • 94. 94 Why use Screenplay? 1. Rich, reusable, reliable interactions 2. Interactions are composable
  • 95. 95 Why use Screenplay? 1. Rich, reusable, reliable interactions 2. Interactions are composable 3. Easy waiting
  • 96. 96 Why use Screenplay? 1. Rich, reusable, reliable interactions 2. Interactions are composable 3. Easy waiting 4. Readable, understandable calls
  • 97. 97 Why use Screenplay? 1. Rich, reusable, reliable interactions 2. Interactions are composable 3. Easy waiting 4. Readable, understandable calls 5. Covers all interactions – not justWeb UI
  • 99. 99 The Screenplay Pattern provides better interactions
  • 100. 100 The Screenplay Pattern provides better interactions for better automation.
  • 101. 101 Actors use Abilities to perform Interactions Actor Ability Interaction
  • 108. 108
  • 109. 109 I’ll mail Boa Constrictor stickers to 10 people who tweet what they learned from this webinar! Include @AutomationPanda & #BoaConstrictor!