The document discusses the Screenplay Pattern, an approach to modeling user interactions in automated tests. It outlines some problems with traditional page object patterns, such as duplication of code and unintuitive structure. The Screenplay Pattern models interactions as tasks performed by actors using their abilities, providing a more intuitive and flexible design. The document then demonstrates implementing the Screenplay Pattern in C# using the Boa Constrictor library, rewriting a web test to search DuckDuckGo in this manner in 3 sentences or less.
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
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();
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();
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.
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
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;
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);
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.