Automation Abstraction Layers: Page Objects and Beyond

Software

alan-richardson
of 48
Description
Text
  1. 1. 1Automation Abstractions:Page Objects and BeyondAlan Richardson@eviltesterhttps://xp-dev.com/svn/AutomationAbstractionswww.SeleniumSimplified.comwww.EvilTester.comwww.CompendiumDev.co.ukwww.JavaForTesters.com
  2. 2. 2What is Abstraction?● Modelling● Separation of concerns● Logical vs Physical● Functional vs Structural● Interfaces vs Implementations● Data / Entities / Persistence● Functionality / Task Flow● Goals / Strategies● Layers – GUI, DB, HTTP● Etc.
  3. 3. “I must create a system. or beenslav'd by another Mans; Iwill not reason & compare:my business is to create”William Blake, 1820Jerusalem: The Emanation of the Giant Albion3http://www.blakearchive.org/exist/blake/archive/object.xq?objectid=jerusalem.e.illbk.10&java=no
  4. 4. https://xp-dev.com/svn/AutomationAbstractions4Example Test WithoutAbstraction@Beforepublic void startDriver(){@Testpublic void canCreateAToDoWithNoAbstraction(){driver.get("http://todomvc.com/architecture-examples/backbone/");int originalNumberOfTodos = driver.findElements(By.cssSelector("ul#todo-list li")).size();WebElement createTodo = driver.findElement(By.id("new-todo"));createTodo.click();createTodo.sendKeys("new task");createTodo.sendKeys(Keys.ENTER);assertThat(driver.findElement(By.id("filters")).isDisplayed(), is(true));int newToDos = driver.findElements(By.cssSelector("ul#todo-list li")).size();assertThat(newToDos, greaterThan(originalNumberOfTodos));}driver = new FirefoxDriver();}@Afterpublic void stopDriver(){driver.close();driver.quit();}NoAbstractionTest.java
  5. 5. But this does use someabstraction layersLLooccaattoorr A AbbssttrraaccttioionnsWebElement Generic Element Abstraction5Example Test WithoutAbstraction@Beforepublic void startDriver(){WebDriver Generic Browser Abstraction@Testpublic void canCreateAToDoWithNoAbstraction(){Firefox Browser Abstractiondriver.get("http://todomvc.com/architecture-examples/backbone/");int originalNumberOfTodos = driver.findElements(By.cssSelector("ul#todo-list li")).size();WebElement createTodo = driver.findElement(By.id("new-todo"));createTodo.click();createTodo.sendKeys("new task");createTodo.sendKeys(Keys.ENTER);assertThat(driver.findElement(By.id("filters")).isDisplayed(), is(true));int newToDos = driver.findElements(Manipulation AbstractionsBy.cssSelector("ul#todo-list li")).size();assertThat(newToDos, greaterThan(originalNumberOfTodos));}driver = new FirefoxDriver();}@Afterpublic void stopDriver(){driver.close();driver.quit();}NoAbstractionTest.java
  6. 6. 6WebDriver provides abstractions● Browser● DOM● Web ElementTool vendors gain value from generic abstractions.You gain value from 'domain' abstractions.
  7. 7. 7Example Test With Abstraction@Beforepublic void startDriver(){@Testpublic void canCreateAToDoWithAbstraction(){TodoMVCUser user =new TodoMVCUser(driver, new TodoMVCSite());user.opensApplication().and().createNewToDo("new task");ApplicationPageFunctional page =new ApplicationPageFunctional(driver,new TodoMVCSite());assertThat(page.getCountOfTodoDoItems(), is(1));assertThat(page.isFooterVisible(), is(true));}driver = new FirefoxDriver();}@Afterpublic void stopDriver(){driver.close();driver.quit();}NoAbstractionTest.java
  8. 8. 8Why Abstraction?● Change implementations● Single Responsibility – only changes whennecessary● Makes automation readable and maintainable● We can unit test some of our test code● etc.https://xp-dev.com/svn/AutomationAbstractions
  9. 9. 9Some Abstraction Layer Categories1) Data– Generic Data Abstractions e.g. email, postcode2) Physical– Physical layout of your application e.g. pages,components– Navigation across pages3) Domain– Your application Entities domain e.g. user, account4) Logical– User actions, workflows
  10. 10. 10Common Automation Abstractions● Page Objects: pages, components, widgets● Dom Element Abstractions: select, textbox, etc.● Domain Objects: user, account, order● Gherkin (Given/When/And/Then)● Domain Specific Languages: code, keywords● ...https://xp-dev.com/svn/AutomationAbstractions
  11. 11. 11Abstraction != Implementation● Abstraction != Tool / Framework /Implementation● Gherkin != Cucumber● Page Object != Page Factory / Slow Loadable● DSL != Keyword DrivenIf we want to get good at abstraction then weneed to model, split apart, make the relationshipsclear, and be aware of our options.
  12. 12. 12Page Objects● The most obviousautomation abstraction● What is it?– A page? A Component?● Do web applications stillhave pages?
  13. 13. A Page Object that abstracts a Page13● Often has methods for– Opening the page– Accessing elements– Doing stuff, and navigating as a side-effect– Logical high level functionality e.g. login– Low level physical functionality e.g.typeUsername, clickLoginButtonShould a Page Object be responsible for all ofthis?
  14. 14. 14Page Object Design Decisions● What methods does it have?– Functional● login(username, password),● loginAs(user)– Structural● enterName(username), enterPassword(password),clickLogin(), submitLoginForm(username, password)● Does it expose elements or not?– public WebElement loginButton;– public WebElement getLoginButton();– public clickLoginButton();
  15. 15. 15Page Object FunctionalityApproaches● Expose WebElements Directly– Leads to WebElement Abstractions– public TextField userNameField;● Hide WebElements behind physical functionalmethods e.g. typeUserName("bob");● Logical helper methods e.g.loginAs(name,pass)● Layers of Page Objects– Physical– Logical
  16. 16. 16Navigation Design Decisions● Does a Page Object return other pages?public IssueListPage submit(){driver.findElement(By.id(“submit”)).click();return new IssueListPage(driver);}● Or Navigate implicitly after interactions● Or have navigation objects
  17. 17. 17Implicit or Explicit Wait?● Implicit Waitdriver.manage().timeouts().implicitlyWait(15L, TimeUnit.SECONDS);assertThat(driver.findElement(By.id("filters")).isDisplayed(), is(true));● Explicit Waitdriver.manage().timeouts().implicitlyWait(0L, TimeUnit.SECONDS);WebDriverWait wait = new WebDriverWait(driver,15);wait.until(ExpectedConditions.elementToBeClickable(By.id("filters")));Example: 'NoAbstractionTest.java'
  18. 18. 18Implementing Page Objects● POJO– Plain object, driver in constructor, methods usedriver.<method>● Page Factory– Annotated elements, lazy instantiation via reflection● LoadableComponent– Common interface (load, isLoaded), isLoadedthrows Error● SlowLoadableComponent– Common interface, waits for on isLoaded● Other approaches?
  19. 19. 19Example Implementationshttps://xp-dev.com/svn/AutomationAbstractions
  20. 20. 20POJO● 'ApplicationPage.java'– Used in 'SequentialCreationOfTest.java'– Not much refactoring in the example● Simple Class● WebDriver passed to constructor● Composition of any components● e.g.– com.seleniumsimplified.todomvc.page.pojo– 'ApplicationPage.java'● Not much refactoring in the example
  21. 21. Pojo Example21
  22. 22. 22Functional vs Structural● Functional– loginAs(username, password)● Structural– enterUsername– enterPassword– clickLoginButton– submitLoginForm(username, password)
  23. 23. 23Functional Vs Structural Example● One way of answering “what methods to put ona page object”– Is it functional / behavioural?– Is it structural / Physical?● Functional 'uses' Structural implementation● Why?– Sometimes it is just a naming difference– Handling exceptions in functional but not structural– Higher level methods in Functional– Different concepts e.g. deleteLastItem
  24. 24. 24Page Factory● Annotate fields with @FindBy● Instantiate in constructor usingPageFactory.initElements– Can create your own page factory initialiser● Avoids boilerplate accessor methods● Fast to create Page Objects● Might become harder to expand and maintain
  25. 25. 25Page Factory Example@FindBy(how = How.CSS, using="#todo-count strong")private WebElement countElementStrong;@FindBy(how = How.CSS, using="#todo-count")private WebElement countElement;@FindBy(how = How.CSS, using="#filters li a")List<WebElement> filters;@FindBy(how = How.ID, using="clear-completed")List<WebElement> clearCompletedAsList;@FindBy(how = How.ID, using="clear-completed")WebElement clearCompletedButton;public ApplicationPageStructuralFactory(WebDriver driver, TodoMVCSite todoMVCSite){PageFactory.initElements(driver, this);this.driver = driver;...
  26. 26. 26SlowLoadableComponent● Some pages and components needsynchronisation to wait till they are ready– One approach – SlowLoadableComponent addsresponsibility for waiting, to the page● extend SlowLoadableComponent● Standard interface for synchronisation on load– get()● If isLoaded then return this Else load● While not loaded{ wait for 200 millseconds}– Implement load and isLoaded
  27. 27. 27Fluent Page ObjectstodoMVC = new ApplicationPageFunctionalFluent(driver, todoMVCSite);todoMVC.get();todoMVC.enterNewToDo("First Completed Item").and().toggleCompletionOfLastItem().then().enterNewToDo("Still to do this").and().enterNewToDo("Still to do this too").then().filterOnCompleted();
  28. 28. 28Fluent Page Objects● Methods return the page object or other objectsinstead of void– void clickDeleteButton();– PageObject clickDeleteButton();● Syntactic sugar methods:– and(), then(), also()● Can work well at high levels of abstraction andwhen no navigation involved● Train Wreck?
  29. 29. Navigation Options29● Direct in Test: page.get(); page.waitFor();● Instantiate new pages based on test flow– Navigation as side-effect, may have to bypass 'get'– Loadable pages, non-loadable, support classes● Page Object methods might return other Pages– e.g. a method on the login page might be● MyAccountPage clickLogin();● We might use navigation objects– direct, Jump.to(MyAccountPage.class)– path based (current page → desired page)● Navigate.to(MyAccountPage.class)
  30. 30. 30Possible Domain Abstractions● Logical Objects– ToDo– ToDoList● Physical Objects– LocallyStoredToDo● Actors– User● Environment– Site– Implementation
  31. 31. 31Page Objects & Domain Objects● Instead of– todoMVC.enterNewToDo(“New Item”)● We could have have– ToDoItem newItem = new ToDoItem(“New Item”);– todoMVC.enterNewToDo(newItem);● See code in DomainBasedTest
  32. 32. 32Domain Objects That Span Logical& Physicale.g. User● user.createNewToDo(“new item”)● user.createNewToDo(newItem)● See use in NoAbstractionTest
  33. 33. 33Sometimes it is possible to overthink this stuff● Don't let thinking about this slow you down● Conduct experiments● Refactor● Rethink if– you are maintaining too much– your abstraction layer stops you doing stuff– you are 'working around' your abstraction layers
  34. 34. 34ElementAbstractions
  35. 35. 35Element Abstraction Examplepublic interface Checkbox {public boolean isChecked();public Checkbox check();public Checkbox uncheck();public Checkbox toggle();}● Would you include 'toggle'?https://xp-dev.com/svn/AutomationAbstractions
  36. 36. 36Element Abstractions● Existing support classes: Select,● Possible: TextBox, Checkbox, TextBox, File etc.● Can enforce Semantics– Checkbox: isChecked, check(), uncheck(), toggle()– TextBox: clear(), enterText()– etc.● Pass back from Page Objects into test?
  37. 37. Element Abstraction Pros and Cons37● May have to create a custom page factory● Can help 'restrict' code i.e. check or uncheck,rather than click, enforces 'semantics'● If you create them...– allow return WebElement● so that I can go beyond the abstraction layer if I need to.Not required if it is just a WebElement wrapper.https://xp-dev.com/svn/AutomationAbstractions
  38. 38. 38Component Abstractions● Shared Components/Widgets on the page● e.g.– VisibleToDoEntry, VisibleToDoList– Filters, Footer, Header, etc.● Could have 'functional' representation forrepeated items e.g. login forms● Could have 'structural' representation● Likely use : page object composition
  39. 39. 39Component Abstraction ExampleTodoEntry todo = page.getToDoEntryAt(lastItemPosition);todo.markCompleted();Instead ofpage.markTodoCompleted(lastItemPosition);
  40. 40. 40Gherkin as an abstraction layerFeature: We can create and edit To Do lists in ToDoMvcWe want to amend todos in ToDoMVC because that isthe set of exercises on the abstraction tutorialScenario: Create a ToDo ItemGiven a user opens a blank ToDoMVC pageWhen the user creates a todo "new task"Then they see 1 todo item on the page● Implement steps using highest appropriateabstraction layer● CucumberJVM as 'DSL implementor'● 'Expressibility' vs 'Step Re-use'● See todomvc.feature and ToDoMvcSteps
  41. 41. 41My modeling biases● Driver– Inject so instantiate any page or component asrequired/desired at any time● Explicit Synchronisation– To make sure that the desired object is availableand ready for use (as defined by synchronisation)● Navigation– Implicit (via taking action e.g. click)– Explicit Navigation Object, not in page object● Open/jump (via driver.get)● To (state model from current, to desired)
  42. 42. 42My modeling biases● Page Objects– Physical● abstract the 'real world'– Components● For common features on the page– Logical● abstract the functionality● Sometimes these are entity methods not page objects– e.g. user.registers().and().logsin()● I tend not to....– abstract WebElements
  43. 43. 43My modeling biases● I tend not to....– abstract WebElements– Use inheritence to create a model of the app● e.g. MyAppPage extends GenericAppPage– Use 3rd party abstractions on top of WebDriver
  44. 44. 44Are there rights and wrongs?● Right / Wrong?● Decisions?● Project/Team/Organisation Standards?Identify your own biases -ExperimentRecognise your decisions
  45. 45. 45Decisions● The 'limits' and overlap of Abstraction Layers● Build it now, or 'refactor to' later● How much change is anticipated?– To which layer? GUI, Business domain, Workflow?● Who is working with the automation code?– Skill levels? Support needed?● How/When with the automation execute?
  46. 46. 46Other Useful Links● Jeff “Cheezy” Morgan – page-object ruby gem,data_magic gem and stareast code– https://github.com/cheezy?tab=repositories● Marcus Merrell– Self-Generating Test Artifacts for Selenium/WebDriver– https://www.youtube.com/watch?v=mSCFsUOgPpw● Anand Ramdeo– One Step at a Time– https://www.youtube.com/watch?v=dFPgzH_XP1Ihttps://xp-dev.com/svn/AutomationAbstractions
  47. 47. 47Homework● Using the code athttps://xp-dev.com/svn/AutomationAbstractions/– Compare the different implementations under 'main'● com.seleniumsimplified.todomvc.page– Investigate how the Page Objects delegate to eachother, and the Domain Objects use Page Objects– Examine the 'test' usage of the Page Objects andDomain Objects– Examine the different navigation approaches
  48. 48. 48Blogs and Websites● CompendiumDev.co.uk● SeleniumSimplified.com● EvilTester.com● JavaForTesters.com● Twitter: @eviltesterOnline Training Courses● Technical Web Testing 101Unow.be/at/techwebtest101● Intro to SeleniumUnow.be/at/startwebdriver● Selenium 2 WebDriver APIUnow.be/at/webdriverapiVideosyoutube.com/user/EviltesterVideosBooksSelenium SimplifiedUnow.be/rc/selsimpJava For Testersleanpub.com/javaForTestersAlan Richardsonuk.linkedin.com/in/eviltesterIndependent Test Consultant& Custom TrainingContact Alanhttp://compendiumdev.co.uk/contact
Comments
Top