SlideShare une entreprise Scribd logo
1  sur  109
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!

Contenu connexe

Tendances

An Introduction to Test Driven Development
An Introduction to Test Driven Development An Introduction to Test Driven Development
An Introduction to Test Driven Development CodeOps Technologies LLP
 
Android JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation ControllerAndroid JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation ControllerLeonardo Pirro
 
Build Your Agile Testing Skill Set
Build Your Agile Testing Skill SetBuild Your Agile Testing Skill Set
Build Your Agile Testing Skill Setlisacrispin
 
TDD And Refactoring
TDD And RefactoringTDD And Refactoring
TDD And RefactoringNaresh Jain
 
Introducing QA Into an Agile Environment
Introducing QA Into an Agile EnvironmentIntroducing QA Into an Agile Environment
Introducing QA Into an Agile EnvironmentJoseph Beale
 
Exploratory Testing Explained
Exploratory Testing ExplainedExploratory Testing Explained
Exploratory Testing ExplainedTechWell
 
Test automation methodologies
Test automation methodologiesTest automation methodologies
Test automation methodologiesMesut Günes
 
Testing concepts ppt
Testing concepts pptTesting concepts ppt
Testing concepts pptRathna Priya
 
Building CRUD Wrappers
Building CRUD WrappersBuilding CRUD Wrappers
Building CRUD WrappersOutSystems
 
Agile Testing - presentation for Agile User Group
Agile Testing - presentation for Agile User GroupAgile Testing - presentation for Agile User Group
Agile Testing - presentation for Agile User Groupsuwalki24.pl
 
End to end testing - strategies
End to end testing - strategiesEnd to end testing - strategies
End to end testing - strategiesanuvip
 
Introduction to Integration Testing With Cypress
Introduction to Integration Testing With CypressIntroduction to Integration Testing With Cypress
Introduction to Integration Testing With CypressErez Cohen
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Developmentguestc8093a6
 

Tendances (20)

An Introduction to Test Driven Development
An Introduction to Test Driven Development An Introduction to Test Driven Development
An Introduction to Test Driven Development
 
Testing methodology
Testing methodologyTesting methodology
Testing methodology
 
Android JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation ControllerAndroid JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation Controller
 
Selenium with java
Selenium with javaSelenium with java
Selenium with java
 
Build Your Agile Testing Skill Set
Build Your Agile Testing Skill SetBuild Your Agile Testing Skill Set
Build Your Agile Testing Skill Set
 
Java Unit Testing
Java Unit TestingJava Unit Testing
Java Unit Testing
 
TDD And Refactoring
TDD And RefactoringTDD And Refactoring
TDD And Refactoring
 
Automation testing
Automation testingAutomation testing
Automation testing
 
Introducing QA Into an Agile Environment
Introducing QA Into an Agile EnvironmentIntroducing QA Into an Agile Environment
Introducing QA Into an Agile Environment
 
Exploratory Testing Explained
Exploratory Testing ExplainedExploratory Testing Explained
Exploratory Testing Explained
 
Test automation methodologies
Test automation methodologiesTest automation methodologies
Test automation methodologies
 
Testing concepts ppt
Testing concepts pptTesting concepts ppt
Testing concepts ppt
 
Building CRUD Wrappers
Building CRUD WrappersBuilding CRUD Wrappers
Building CRUD Wrappers
 
Automation testing
Automation testingAutomation testing
Automation testing
 
Agile Testing - presentation for Agile User Group
Agile Testing - presentation for Agile User GroupAgile Testing - presentation for Agile User Group
Agile Testing - presentation for Agile User Group
 
End to end testing - strategies
End to end testing - strategiesEnd to end testing - strategies
End to end testing - strategies
 
Introduction to Integration Testing With Cypress
Introduction to Integration Testing With CypressIntroduction to Integration Testing With Cypress
Introduction to Integration Testing With Cypress
 
Git cheatsheet
Git cheatsheetGit cheatsheet
Git cheatsheet
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
Agile testing
Agile testingAgile testing
Agile testing
 

Similaire à The Screenplay Pattern for Better Automated Tests

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 TestingJohn Ferguson Smart Limited
 
Google
GoogleGoogle
Googlesoon
 
GDG İstanbul Şubat Etkinliği - Sunum
GDG İstanbul Şubat Etkinliği - SunumGDG İstanbul Şubat Etkinliği - Sunum
GDG İstanbul Şubat Etkinliği - SunumCü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 seleniumXebia IT Architects
 
Experienced Selenium Interview questions
Experienced Selenium Interview questionsExperienced Selenium Interview questions
Experienced Selenium Interview questionsarchana 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 seleniumKhyati 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 84Mahmoud 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 Seleniumvaluebound
 
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 pageobjectsLeticia Rss
 
Taiko - Reliable Browser Automation
Taiko - Reliable Browser AutomationTaiko - Reliable Browser Automation
Taiko - Reliable Browser AutomationHarmeet Singh
 
Demystifying Keyword Driven Using Watir
Demystifying Keyword Driven Using WatirDemystifying Keyword Driven Using Watir
Demystifying Keyword Driven Using WatirHirday Lamba
 
Complete_QA_Automation_Guide__1696637878.pdf
Complete_QA_Automation_Guide__1696637878.pdfComplete_QA_Automation_Guide__1696637878.pdf
Complete_QA_Automation_Guide__1696637878.pdframya9288
 
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 2015Naresha K
 
Testing web application with Python
Testing web application with PythonTesting web application with Python
Testing web application with PythonJachym 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 sideMek Srunyu Stittri
 
Testing ASP.NET - Progressive.NET
Testing ASP.NET - Progressive.NETTesting ASP.NET - Progressive.NET
Testing ASP.NET - Progressive.NETBen Hall
 

Similaire à The Screenplay Pattern for Better Automated Tests (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
 

Plus de 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 + KobitonApplitools
 
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 UIApplitools
 
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 FutureApplitools
 
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 CuriosityApplitools
 
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 AutomationApplitools
 
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 TeamsApplitools
 
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 FunctionalityApplitools
 
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 TeamsApplitools
 
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.pdfApplitools
 
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 CentraApplitools
 
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 FutureApplitools
 
Getting Started with Visual Testing
Getting Started with Visual TestingGetting Started with Visual Testing
Getting Started with Visual TestingApplitools
 
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 CypressApplitools
 
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 ApplicationsApplitools
 
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 DeliveryApplitools
 
AI-Powered-Cross-Browser Testing
AI-Powered-Cross-Browser TestingAI-Powered-Cross-Browser Testing
AI-Powered-Cross-Browser TestingApplitools
 
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 JavascriptApplitools
 

Plus de 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
 

Dernier

W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfCionsystems
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Test Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendTest Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendArshad QA
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 

Dernier (20)

W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdf
 
Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Test Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendTest Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and Backend
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 

The Screenplay Pattern for Better Automated Tests

  • 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!