ADF and Selenium Component Based Unit Testing
ADF and SeleniumComponent Based Unit Testing
About Us
Richard Olrichs
MN
www.olrichs.nl
@richardolrichs
Wilfred van der Deijl
The Future Group
www.redheap.com
@wilfreddeijl
AgendaDemo: 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 interactionVery 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 Toolsgithub.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 withADF 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 ObjectSeleniumTools
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 ObjectSeleniumTools
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 methodsfindAdfComponent uses (relative) JSF selectors
Acquiring ADF ComponentSeleniumTools
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 Componentsimport 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 elementsSelenium Actions can interact with browser and mouseAdfComponent.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 componentComponent classes frequently use javascript to interact with componentsAdfComponent base class offers protected executeScript methodEvery component becomes a client component withoracle.adf.view.rich.automation.ENABLED=true in web.xml
Interacting with ADF ComponentsSeleniumTools
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: AdfTablelong 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 RecapJUnit Test your taskflows
Use TaskFlow Tester for isolated tests - java.net/projects/adf-task-flow-tester
PerceptualDiffMatcher - bit.ly/pdiffor look at more powerful Depicted at github.com/bslatkin/dpxdt
JaCoCo Code CoverageJUnit Rule to dump execution data - bit.ly/jacoco-ruleOptional reporter to write html report - bit.ly/jacoco-report
Browser of choice: Firefox, PhantomJS or any other...
Resources
github.com/wvanderdeijl/adf-seleniumwww.seleniumhq.org - mostly v1 docsseleniumhq.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