140 likes | 338 Views
and the PageObject Design Model. Automating with Selenium 2. How Selenium Remote Control works You launch a server on your test machine. Your tests connect to that server via IP. The server launches a browser, with selenium CORE embedded as javascript into the page.
E N D
and the PageObject Design Model Automating with Selenium 2
How Selenium Remote Control works • You launch a server on your test machine. • Your tests connect to that server via IP. • The server launches a browser, with selenium CORE embedded as javascript into the page. • Selenium CORE simulates user actions with javascript. SeleniumYou can’t be good at everything
The Good The Bad • Doesn’t steal your mouse/keyboard. • Works with any browser that uses javascript • Works for any OS that supports java. • Very fast page interactions. • Large API • Supports a variety of programming languages. • Can be run on remote machines • Can’t see anything outside the page object. • Not all browsers support all functionality. • Some pages aren’t automatable. • Can’t see inside third party apps • XSS Limitations The javascript sandbox
A different way of automating the browser. • Create a browser-specific driver to control the browser directly. • Have to do this for each browser! • Object oriented API • Doesn’t need a real browser • No javascript limitations • No need for a server. • Isn’t as delicate as selenium. WebDriver Introduction
Went into Beta Dec 24th. • WebDriver + Selenium • The two projects have merged (literally) into Selenium 2.0 • Large browser support and no javascript limitations. • No server • The old API’s are still available. • New API’s are becoming available. Selenium 2
You have 2 options: • IWebDriver • This is just the WebDriver api • Doesn’t support a lot of browsers. • Will need to change all your tests. • WebDriverBackedSelenium • Uses the old Selenium 1 API • But uses WebDriver to run things if possible • Can use selenium 1 if the browser isn’t supported. Automating with Selenium 2
Object Oriented • Doesn’t break nearly as often • Handles pageloads automatically • Fewer problems automating • Somewhat more complicated API • selenium.type(“password”,”thisIsMyPassword”); • driver.findElement(By.id("password")).sendKeys(“thisIsMyPassword"); • By.Id, By.Xpath, By.Name, By.ClassName, By.PartialLinkText • All the supported browsers work really well • Can extend the API to add custom functionality. • Works well with a Page Object design model. The WebDriver API
Adds a layer of abstraction into your code. Helps to organize your code once it grows large. All automation is automatically reusable and shareable. A way to separate tests from re-usable functions. A way to store information about how the system works. A way to specify what page functions start on, and what page they end on. A way to programmatically break your tests when functionality changes. Makes code maintenance easier. There is even a PageFactory class available to automatically create them. Page Object Design Pattern
Each page is defined as it’s own class. • Actions (including navigation) are represented as functions for a class. • Each function returns a new Page object, signifying what page the actions stops on. • Your tests “know” what page you are on, and will only give you access to functions available to that class. • Tests only talk to the page objects. • Page objects only talk to the driver. • Elements on the page are stored as variables for the page object. • Automatic page validations can be stored in the constructor for each page object. • Tests become a string of well defined functions, not meaningless gibberish. • Tests can be grouped by namespace. • Class Inheritance can be used to define functionality to a set of pages. • We can make functional logic transparent to the tests by returning different inherited classes. How does it work?
globalVars.logDescription = "log in"; globalVars.selenium.Open(globalVars.mobiUrl); functions.type("userId", globalVars.studName + "1"); functions.type("password", globalVars.studPass + "1"); functions.clickAndWait("signInBtn"); selenium.click("//a[@id='discussions']/span"); selenium.click("//a[@id='thingsToKnow']/span"); globalVars.logDescription = "Checking elements on happenings:by date page"; selenium.waitForElementNotVisible("//div[@id='THSContainer']//span[@class='ajaxLoadingHeader']"); selenium.waitForElementVisible("//div[@id='THSContainer']/ul[1]/li[1]"); selenium.click("//div[@id='THSContainer']//span[@class='replytext']"); selenium.click("backButton"); selenium.waitForElementVisible("//div[@id='TTHContainer']/ul[1]/li[1]"); selenium.click("//div[@id='TTHContainer']//span[@class='replytext']"); selenium.click("backButton"); globalVars.selenium.Select("byDateFilter", "label=Things Happening Soon"); selenium.waitForElementVisible("//div[@id='THSContainer']/ul[1]/li[1]"); selenium.click("//div[@id='THSContainer']//span[@class='replytext']"); selenium.click("backButton"); globalVars.selenium.Select("byDateFilter", "label=Things That Happened"); Bad Tests
[Test] public void testByDateTab() { funtions.loginMobi(); selenium.click("//a[@id='discussions']/span"); selenium.click("//a[@id='thingsToKnow']/span"); functions.verifyThingsToKnow(); functions.verifyThingsHappeningSoon(); selenium.Select("byDateFilter", "label=Things That Happened"); functions.verifyThingsThatHappened(); } Better Tests
[Test] public void testByDateTab() { selenium.Open(Moby_Common.MobyLoginUrl); LoginPage loginPage = new LoginPage(selenium); HappeningsPage happeningsPage = loginPage.loginAs(Common.stud1Name, Common.stud1Password); happeningsPage.waitToLoad(); Assert.That(!happeningsPage.isByTypePageLoaded()); Assert.That(happeningsPage.isByDatePageLoaded()); Assert.That(!happeningsPage.isByCoursePageLoaded()); happeningsPage.filterResults("byDateFilter","Things That Happened"); Assert.That(happeningsPage.isVisible("TTHContainer")); happeningsPage.filterResults("byDateFilter", "Things Happening Soon"); Assert.That(happeningsPage.isVisible("THSContainer")); happeningsPage.filterResults("byDateFilter", "All Types"); Assert.That(happeningsPage.isVisible("TTHContainer")); Assert.That(happeningsPage.isVisible("THSContainer")); } Good Tests
The Page Object: public class HappeningsPage : WebPageBaseClass { private string _loadingImage = "//span[@class='ajaxLoadingHeader']"; private string _moreLink = "more"; public HappeningsPage(ISelenium selenium) { this.selenium = selenium; this.title = "Happenings"; this.url = "index.html"; assertPageLoadedCorrectly(); } public HappeningsPage waitToLoad() { waitForElementNotVisible(_loadingImage ); return new HappeningsPage(selenium); } public ContentItemPage goToItem(string type, string name) { click("//div[@id='" + type + "']//span[text()=\"" + name + "\"]"); return new ContentItemPage(selenium); } public HappeningsPage clickMoreLink() { click(_moreLink); return new HappeningsPage(selenium); } public HappeningsPage filterResults(string id, string name) { selectDropdown(id, name); return new HappeningsPage(selenium); } }