ADF and Selenium

advertisement
ADF and Selenium
Component Based Unit Testing
About Us
Richard Olrichs
Wilfred van der Deijl
MN
The Future Group
www.olrichs.nl
www.redheap.com
@richardolrichs
@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
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());
}
Basic JUnit Example
ADF Component 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());
}
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?
Acquiring ADF Page Object
SeleniumTools
Selenium
CustomCode
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 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?
Acquiring ADF Component
SeleniumTools
Selenium
CustomCode
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
Interacting with ADF Components
How did we implement
@Test
component methods?
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());
}
Interacting with ADF Components
import com.redheap.selenium.component.AdfComponent;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
SeleniumTools
Selenium
CustomCode
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
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
SeleniumTools
Selenium
CustomCode
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
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
Not all paths tested
JaCoCo Code Coverage
shows this method wasn’t
covered in test
Screenshot Diff Assertion Violation
Tested application looks different
during test compared to “known
good reference”
“Known good
reference”
Actual screenshot
during test
Diffs indicated
Download