ADF and Selenium Component Based Unit Testing
About Us Richard Olrichs MN www.olrichs.nl @richardolrichs Wilfred van der Deijl The Future Group www.redheap.com @wilfreddeijl
Agenda Demo: Selenium Plain Selenium Examples Page Objects Demo: ADF Selenium ADF Selenium Tools Demo: Testing Your Bounded Taskflows
Selenium 101 Demo
Selenium 101 public void simpleTest() { WebDriver driver = new FirefoxDriver(); driver.get("http://google.com/?hl=en"); WebElement searchBox = driver.findElement(name("q")); searchBox.sendKeys("adf selenium"); searchBox.submit(); }
Selenium History Selenium v1 ● Uses JavaScript injection to emulate user interaction Very flaky with modern apps ● Used in OTN Article (don’t do that) Selenium v2 (aka WebDriver) ● Native events to drive browser
Page Objects
Page Objects Martin Fowler: “It should provide an interface that's easy to program to and hides the underlying widgetry in the window” Source: martinfowler.com/bliki/PageObject.html Also advocated by Selenium team
ADF Selenium Tools
ADF Selenium Tools github.com/wvanderdeijl/adf-selenium Two JDev 12c Projects: ● SeleniumTools - Library JAR ● RichClientDemoTest - Sample JUnit tests against ADF 12c component demo
ADF Selenium Demo
Basic JUnit Example @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); }
Basic JUnit Example @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } Page Object
Basic JUnit Example @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } ADF Component Object
Basic JUnit Example @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } Interact with ADF Component
Basic JUnit Example @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } Test Assertion
Acquiring ADF Page Object @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } ● How to start browser? ● How to navigate to this page?
public class CalendarTest { @ClassRule public static WebDriverResource driver = new FirefoxDriverResource(); WebDriverResource starts (and stops) a web browser to run the tests @ClassRule: invoke JUnit rule once per test class @Rule: invoke JUnit rule for each test method Acquiring ADF Page Object SeleniumTools Selenium CustomCode
public class CalendarTest { @ClassRule public static WebDriverResource driver = new FirefoxDriverResource(); @Rule public PageProvider<CalendarDemoPage> pages = new PageProvider(CalendarDemoPage.class, PAGE_URL, driver.getDriver()); PageProvider takes a WebDriver (browser) and knows how to navigate to a URL and instantiate a Page Object @Rule triggers provider for each test so we start fresh Acquiring ADF Page Object SeleniumTools Selenium CustomCode
Acquiring ADF Component @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } How does a Page Object locate components?
import com.redheap.selenium.component.AdfCalendar; import com.redheap.selenium.page.Page; public class CalendarDemoPage extends Page { public AdfCalendar findCalendar() { return findAdfComponent("dmoTpl:cal"); } com.redheap.selenium.page.Page base class offers protected utility methods findAdfComponent uses (relative) JSF selectors Acquiring ADF Component SeleniumTools Selenium CustomCode
Interacting with ADF Components @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } How did we implement component methods?
Interacting with ADF Components import com.redheap.selenium.component.AdfComponent; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; public class AdfCalendar extends AdfComponent { public void hoverActivityInView(int index) { WebElement element = findAllActivitiesInView().get(index); // move mouse to element and wait for ADF to detect hover new Actions(getDriver()).moveToElement(element).pause(1000).perform(); waitForPpr(); } } Component class encapsulates interaction with HTML elements Selenium Actions can interact with browser and mouse AdfComponent.waitForPpr waits for any PPR and complete javascript processing SeleniumTools Selenium CustomCode
Interacting with ADF Components @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } How did we implement component methods?
import com.redheap.selenium.component.AdfComponent; public class AdfOutputText extends AdfComponent { public Object getValue() { return executeScript("var cmp=AdfPage.PAGE.findComponentByAbsoluteId(arguments[0]);" + "return cmp.getValue()", getClientId()); } } CalendarDemoPage.findPopupNote returns AdfOutputText component Component classes frequently use javascript to interact with components AdfComponent base class offers protected executeScript method Every component becomes a client component with oracle.adf.view.rich.automation.ENABLED=true in web.xml Interacting with ADF Components SeleniumTools Selenium CustomCode
Selenium Tools Component Classes ● Re-use a lot of logic that would otherwise live in Page Objects ● Rely heavily on ADF JavaScript API ● All extend from AdfComponent ○ click(), contextClick(), doubleClick() ○ dragAndDropTo(AdfComponent target) ○ findAdfComponent(String childId) ○ isDisabled(), isDisplayed() ○ scrollIntoView() ○ and a few more
Component Class Example: AdfTable long getRowCount() findAdfComponent(String child, int row) scrollToRowIndex(int row) discloseRowDetail(int row) List<Integer> getDisclosedRows() selectRow(int row) selectToRow(int row) selectAdditionalRow(int row) ... and all AdfComponent methods
Testing Your Bounded Taskflows
Bounded Taskflow Recap JUnit Test your taskflows Use TaskFlow Tester for isolated tests - java.net/projects/adf-task-flow-tester PerceptualDiffMatcher - bit.ly/pdiff or look at more powerful Depicted at github.com/bslatkin/dpxdt JaCoCo Code Coverage JUnit Rule to dump execution data - bit.ly/jacoco-rule Optional reporter to write html report - bit.ly/jacoco-report Browser of choice: Firefox, PhantomJS or any other...
Resources github.com/wvanderdeijl/adf-selenium www.seleniumhq.org - mostly v1 docs seleniumhq.github.io/docs/ - new v2 docs
Demo shots (reference material)
Example Test Start test in Taskflow Tester Basic assertions Compare screenshot
Taskflow running in Taskflow Tester
Validation Error triggered by test
JUnit Test Runner in JDeveloper
JaCoCo Code Coverage Report
JaCoCo Code Coverage shows this method wasn’t covered in test Not all paths tested
Screenshot Diff Assertion Violation Tested application looks different during test compared to “known good reference”
“Known good reference”
Actual screenshot during test
Diffs indicated

Automated Testing ADF with Selenium

  • 1.
    ADF and Selenium ComponentBased Unit Testing
  • 2.
    About Us Richard Olrichs MN www.olrichs.nl @richardolrichs Wilfredvan der Deijl The Future Group www.redheap.com @wilfreddeijl
  • 3.
    Agenda Demo: Selenium Plain SeleniumExamples Page Objects Demo: ADF Selenium ADF Selenium Tools Demo: Testing Your Bounded Taskflows
  • 4.
  • 5.
    Selenium 101 public voidsimpleTest() { WebDriver driver = new FirefoxDriver(); driver.get("http://google.com/?hl=en"); WebElement searchBox = driver.findElement(name("q")); searchBox.sendKeys("adf selenium"); searchBox.submit(); }
  • 6.
    Selenium History Selenium v1 ●Uses JavaScript injection to emulate user interaction Very flaky with modern apps ● Used in OTN Article (don’t do that) Selenium v2 (aka WebDriver) ● Native events to drive browser
  • 7.
  • 8.
    Page Objects Martin Fowler: “Itshould provide an interface that's easy to program to and hides the underlying widgetry in the window” Source: martinfowler.com/bliki/PageObject.html Also advocated by Selenium team
  • 9.
  • 10.
    ADF Selenium Tools github.com/wvanderdeijl/adf-selenium TwoJDev 12c Projects: ● SeleniumTools - Library JAR ● RichClientDemoTest - Sample JUnit tests against ADF 12c component demo
  • 11.
  • 12.
    Basic JUnit Example @Test publicvoid testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); }
  • 13.
    Basic JUnit Example @Test publicvoid testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } Page Object
  • 14.
    Basic JUnit Example @Test publicvoid testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } ADF Component Object
  • 15.
    Basic JUnit Example @Test publicvoid testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } Interact with ADF Component
  • 16.
    Basic JUnit Example @Test publicvoid testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } Test Assertion
  • 17.
    Acquiring ADF PageObject @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } ● How to start browser? ● How to navigate to this page?
  • 18.
    public class CalendarTest{ @ClassRule public static WebDriverResource driver = new FirefoxDriverResource(); WebDriverResource starts (and stops) a web browser to run the tests @ClassRule: invoke JUnit rule once per test class @Rule: invoke JUnit rule for each test method Acquiring ADF Page Object SeleniumTools Selenium CustomCode
  • 19.
    public class CalendarTest{ @ClassRule public static WebDriverResource driver = new FirefoxDriverResource(); @Rule public PageProvider<CalendarDemoPage> pages = new PageProvider(CalendarDemoPage.class, PAGE_URL, driver.getDriver()); PageProvider takes a WebDriver (browser) and knows how to navigate to a URL and instantiate a Page Object @Rule triggers provider for each test so we start fresh Acquiring ADF Page Object SeleniumTools Selenium CustomCode
  • 20.
    Acquiring ADF Component @Test publicvoid testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } How does a Page Object locate components?
  • 21.
    import com.redheap.selenium.component.AdfCalendar; import com.redheap.selenium.page.Page; publicclass CalendarDemoPage extends Page { public AdfCalendar findCalendar() { return findAdfComponent("dmoTpl:cal"); } com.redheap.selenium.page.Page base class offers protected utility methods findAdfComponent uses (relative) JSF selectors Acquiring ADF Component SeleniumTools Selenium CustomCode
  • 22.
    Interacting with ADFComponents @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } How did we implement component methods?
  • 23.
    Interacting with ADFComponents import com.redheap.selenium.component.AdfComponent; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; public class AdfCalendar extends AdfComponent { public void hoverActivityInView(int index) { WebElement element = findAllActivitiesInView().get(index); // move mouse to element and wait for ADF to detect hover new Actions(getDriver()).moveToElement(element).pause(1000).perform(); waitForPpr(); } } Component class encapsulates interaction with HTML elements Selenium Actions can interact with browser and mouse AdfComponent.waitForPpr waits for any PPR and complete javascript processing SeleniumTools Selenium CustomCode
  • 24.
    Interacting with ADFComponents @Test public void testHover() { CalendarDemoPage page = pages.goHome(); AdfCalendar calendar = page.findCalendar(); calendar.hoverActivityInView(0); assertEquals("NOTE: This popup is for demo purposes only;...", page.findPopupNote().getValue()); } How did we implement component methods?
  • 25.
    import com.redheap.selenium.component.AdfComponent; public classAdfOutputText extends AdfComponent { public Object getValue() { return executeScript("var cmp=AdfPage.PAGE.findComponentByAbsoluteId(arguments[0]);" + "return cmp.getValue()", getClientId()); } } CalendarDemoPage.findPopupNote returns AdfOutputText component Component classes frequently use javascript to interact with components AdfComponent base class offers protected executeScript method Every component becomes a client component with oracle.adf.view.rich.automation.ENABLED=true in web.xml Interacting with ADF Components SeleniumTools Selenium CustomCode
  • 26.
    Selenium Tools ComponentClasses ● Re-use a lot of logic that would otherwise live in Page Objects ● Rely heavily on ADF JavaScript API ● All extend from AdfComponent ○ click(), contextClick(), doubleClick() ○ dragAndDropTo(AdfComponent target) ○ findAdfComponent(String childId) ○ isDisabled(), isDisplayed() ○ scrollIntoView() ○ and a few more
  • 27.
    Component Class Example:AdfTable long getRowCount() findAdfComponent(String child, int row) scrollToRowIndex(int row) discloseRowDetail(int row) List<Integer> getDisclosedRows() selectRow(int row) selectToRow(int row) selectAdditionalRow(int row) ... and all AdfComponent methods
  • 28.
  • 29.
    Bounded Taskflow Recap JUnitTest your taskflows Use TaskFlow Tester for isolated tests - java.net/projects/adf-task-flow-tester PerceptualDiffMatcher - bit.ly/pdiff or look at more powerful Depicted at github.com/bslatkin/dpxdt JaCoCo Code Coverage JUnit Rule to dump execution data - bit.ly/jacoco-rule Optional reporter to write html report - bit.ly/jacoco-report Browser of choice: Firefox, PhantomJS or any other...
  • 31.
  • 32.
  • 33.
    Example Test Starttest in Taskflow Tester Basic assertions Compare screenshot
  • 34.
  • 35.
  • 36.
    JUnit Test Runnerin JDeveloper
  • 37.
  • 38.
    JaCoCo Code Coverage showsthis method wasn’t covered in test Not all paths tested
  • 39.
    Screenshot Diff AssertionViolation Tested application looks different during test compared to “known good reference”
  • 40.
  • 41.
  • 42.