Sunday, February 8, 2015

Testing Oracle ADF with Selenium WebDriver Page Objects

Selenium is an awesome (and free) tool to automate browser-based user interface testing. Selenium offers two ways of working; Selenium IDE; a firefox add-on for simple record-and-playback of interactions with your browser and Selenium WebDriver; a collection of language specific bindings to drive a browser -- the way it is meant to be driven.

For testing ADF applications you'll need to use Selenium WebDriver as that sends native events to the browser it is controlling. This means it will fire actual mouse, keyboard, and other events, which is something the javascript-heavy ADF framework needs.

I've built an example on how to test the public Oracle ADF Faces 12c Components Demo for a real life demo using JUnit and Selenium WebDriver. The example project is at github and I'll explain the important parts in a number of blog posts. This first post will show how the JUnit test classes interact with the page objects. The nitty gritty details on how to work with ADF and Selenium will be covered in separate posts.

My advice is to follow the Page Objects Pattern proposed by the Selenium team. It creates a clear separation between Page Objects and Test Objects. Page Objects know about the HTML, the page components and how to interact with them while the Test Objects drive these page objects and contain the real test logic and assertions. An alternative approach would be the Bot Style tests which combines page logic and Selenium interactions in a single class.

In an ideal world making changes to the ADF page itself only involves updating the Page Objects while the Test Objects can remain the same. Unless you're actually changing functionality on the page that requires changes to the tests itself. But even then it helps you to separate these two objects. The Page Object is more "developer oriented", while the Test Object is more "tester oriented".

Let's look at an example that drives the public Oracle ADF Faces Components Demo:

@Test
public void testNavigationToFileExplorer() throws Exception {
    FileExplorer page = getPage().clickFileExplorerLink().clickTreeTableTab();
    page.getScreenshotAs(new ScreenshotFile(new File("explorer-tree-table.png")));
}

This is a JUnit test method in the RichClientDemoTest test class. It starts in line 3 by navigation to the component demo homepage by invoking getPage(). This method returns a RichClientDemo page object. This is one of the aforementioned page objects that allow interaction with the web page.
ADF 12c Rich Client demo page with File Explorer link highlighted
Invoking the clickFileExplorerLink() method on the RichClientDemo page object will find the link on the page to navigate to the File Explorer demo and click that link to navigate to that page. Navigation methods on page objects will return other page objects. In this example we invoked a navigation method on RichClientDemo which returns a FileExplorer page object.
File Explorer page with Tree Table tab highlighted
Finally we invoke the clickTreeTableTab() method to click on the Tree Table tab in the right hand side af:pannelTabbed. Methods on Page Objects return the same page object instance or another when navigation occurs. In either case this means you can chain numerous of these methods on a single line when needed.

The last line of the test method shows how you can take a screenshot of what the browser is showing at the time. This can be very helpful especially with failing test cases where it could be useful to have a screenshot of the failed state.
Screenshot taken by Firefox ran by Selenium WebDriver

This example showed how to interact with page objects and it will fail if any of the required links or other elements are not available on the page. But it doesn't have any real test assertions yet. One final example shows how to assert that the number of expanded nodes in an af:tree should have increased when clicking one of the (collapsed) nodes in the tree:

@Test
public void testExpandTagGuideNodeA() {
    RichClientDemo page = getPage();
    int expandedNodesBefore = page.getTagGuideTreeExpandedNodeCount();
    page.clickLayoutTreeNode();
    Assert.assertEquals("number of expanded node should increase", 
                        expandedNodesBefore + 1,
                        page.getTagGuideTreeExpandedNodeCount());
}

Line 3 shows how we again first navigate to the rich client demo homepage as that is the starting point for all of our tests. Line 4 uses the RichClientDemo page object to retrieve the number of expanded nodes in the af:tree component. Notice how the knowledge on how to interact with the page to get this information is contained in the page objects and not exposed to the tester. Line 5 clicks the node in the tree labeled Layout. Again notice the knowledge on how to locate and click this node is hidden from the tester class. Finally in line 6 we have a JUnit assertion that the number of expanded nodes in the tree should have been increased by 1. If that is not the case this JUnit test will fail.

By the way; a Page Object doesn't have to represent a full page. Especially with ADF regions it is conceivable that each region would have its own Page Object (or rather Page Fragment Object). For complex page fragments this could be split up even further to keep the code manageable. More on that in other blog posts. I'll explain the other bits and pieces of this demo in separate blog posts. If you want to look at the full demo just hop over to github.

This post is part of a series on how to get Selenium to work with Oracle ADF.

2 comments:

  1. Hello Wilfred, Than you very much for this serie of posts about Selenium which are very interesting. I was given up selenium because too much problems with "one page application" but you give me back hope !
    Have you got a solution for the problem described here https://community.oracle.com/thread/1053605
    When you launch bounded task flows using an inline-popup, the id of the created iFrame changes with each call of the bounded task flow. This in turn means that the selenium SelectFrame command fails.
    Best regards,
    Christophe,

    ReplyDelete
    Replies
    1. I am always up for a challenge and would love to integrate this in the upcoming sample selenium application. What are you trying to do; have an af:region in a af:panelWindow in an af:popup or are you doing a taskflow call while showing the called taskflow in a popup?

      Delete

Comments might be held for moderation. Be sure to enable the "Notify me" checkbox so you get an email when the comment is accepted and I reply.