Selenium Page Objects and Abstraction

Jan 23, 2012 by

Last Friday, I explained the concept of Selenium Page Objects to my colleague and I feel like I didn’t explain it in a simple way enough. I have spent some time today to come up with explanation that relates the page object pattern to the concept of abstraction in programming

Object Abstraction

Let’s say we have a class that processes news messages

class NewsListener{
    public void onNewsMessage(String rawMsg){
        //Example of raw message; "ID1234:Alert-News content"
        String newsId = rawMsg.substring(0 , rawMsg.indexOf("-"));

        if( newsId.substring( newsId.indexOf(":") + 1 ).equals("Alert") ){
            notifyAlertListener(newsId, rawMsg);
        }else{
            renderNewsContent(newsId, rawMsg);
        }
    }
}

It seems like the NewsListener class know the news raw message really well. But the class is unlikely to be the only one that needs to get news ID and message type out of the raw message; AlertListener and NewsContentPanel may also need to use those values too. Although, those two lines of raw message parsing seems to be easy enough to be copied to all other classes but any decent developers know that will definitely causes problems

  • Tight Coupling and Code Duplication – This is obvious, the parsing code is copied to every place that need to know message ID and message type. If the format of the message has changed then every class needs to be modified
  • Wrong level of abstraction – The main business logic of the class is to dispatch message to the right listener according to message type. It should not care how the type is derived from the message

We can improve the code by introduce an abstraction; NewsMessage, to encapsulate the internal message format in one place and expose just the information that need to be used by other classes

public void onNewsMessage(NewsMessage msg){
        if( msg.isAlert() ){
            notifyAlertListener(msg);
        }else{
            renderNewsContent(msg);
        }
}


Selenium Page Objets

Let’s look at a test case to verify that the target page will not show any project information when there is no project stored in database

public class ListProjectPageTest {
    private static WebDriver driver;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        driver = new FirefoxDriver();
    }
    @Test
    public void listProjectsWhenNoProjectInDB(){
        driver.get(  URLConfig.get(“list-project-page”)  );
        WebElement table = findElementIgnoreException( By.cssSelector("#container > table") );
        int numberOfProjShowing = table.findElements( By.xpath("tbody/tr") ).size();
        assertEquals(0, numberOfProjShowing );
    }
}

Just like the NewsListener example, the test just want to assert the number of projects that have been showed on the target page but it choose to use Selenium driver to get the information directly from HTML DOM node. This code for retrieving the number of rows must be copied into every test case that needs the same information and the changes in HTML structure will risk breaking all those test cases

Page Objects is a pattern to capture web UI components into object. We can create ProjectsPage to encapsulate all the logic to get project’s information in one place and let our test cases work with our web UI at the higher level of abstraction

@Test
public void listProjectsWhenNoProjectInDB(){
ListProjectsPage page = ListProjectsPage.navigateToPage(driver);
	assertEquals(0, page.getNumberOfProjectsShowing() );
}

Below is example of how the page objects could be implemented

public class ListProjectsPage{
	private final WebDriver driver;
	private final WebElement tableElem;

	private ListProjectsPage(WebDriver driver) {
		tableElem = findElementIgnoreException( By.cssSelector("#container > table") );
	}

	public static ListProjectsPage navigateToPage(WebDriver driver){
		driver.get( getURL("list-projects-page") );
		return new ListProjectsPage(driver);
	}

	public int getNumberOfProjectsShowing(){
		return  tableElem.findElements( By.xpath("tbody/tr") ).size();
	}
}

The pattern will make test cases less brittle and easy to read. Test cases will come close to the promise of “executable requirement”

read more

Related Posts

Share This

Java Deflater and OutOfMemoryError

Aug 2, 2011 by

Summary: Relying on Finalizer to clean up native memory of Deflater object may cause OutOfMemoryError

I guess this is old for many developers but it is new to me. I have been asked to join an investigation of OutOfMemoryError issue occurred in a project outside my division. Actually, it was a brief involvement. My colleague who was handling the issue already got a possible root cause. He believed that it’s something about the compressing code that just had been added in the newest release. The compressing logic used java.util.zip.Deflater and the end() method of Deflater objects hadn’t been explictly closed once the compressing was done. Cleaning up mechanism relied on Java Finalizer to call finalize() method of Deflater objects

My colleague suspected that a Deflater object consumed a little space in Java heap but also allocated much bigger space of native memory. He believed it was possible that a large number of Deflater objects had been created which caused OOM in native side. Most of the Deflater objects were unreachable but GC didn’t kick in because overall heap consumption was still low. The result was that all those unreachable objects hadn’t been scheduled for Finalizer yet so all the native memory allocations were still retained

I was skeptical of his assumption at first. I thought if JVM was being under the risk of OOM then way didn’t GC try to reclaim java heap to let the object that allocated native memory be cleaned up. I decided to write a simple code to prove the scenario


public static void main(String[] args) throws IOException {
        int testSize = 1;
        if(args.length > 0){ testSize = Integer.parseInt(args[0]); }

        String content = readConent( Main.class.getResourceAsStream("data.txt"));
        byte[] input = content.getBytes("UTF-8");

        System.out.println("Test size = " + testSize);
        for(int i=0; i< testSize; i++){
            byte[] output = new byte[500];
            Deflater deflater = new Deflater();
            deflater.setInput(input);
            deflater.finish();
            deflater.deflate(output);
            //deflater.end();
        }

        System.out.println("Done");

        //Let it run for a while so I can monitor the java process.
        try{ Thread.sleep(60*60*1000); }catch(InterruptedException ex){}
    }

The program repeatedly compresses a short string (around 300 bytes). Notice that the line deflater.end() has been commented out which will defer the cleaning up process until the Finalizer thread start looking at the objects

I got OOM when I ran the program to create 6300 instances of Deflater

java -cp build\classes -verbose:gc -Xmx300m -Xms300m deflat.Main 6300

Exception in thread “main” java.lang.OutOfMemoryError
at java.util.zip.Deflater.init(Native Method)
at java.util.zip.Deflater.(Deflater.java:123)
at java.util.zip.Deflater.(Deflater.java:140)
at deflat.Main.main(Main.java:23)

I ran the program with –verbose:gc to check whether GC would try to collect objects in heap before OOM occurred or not. There was no GC activity logged to console at all. My colleague’s assumption was right; GC are not able to aware that native memory usage is about to exceed limit

I ran the program again with test size of 6200 to let it finish execution successfully then checked the current heap size

“C:\Program Files\Java\jdk1.6.0_13\bin\jstat.exe” -gc 2936

S0C          S1C        S0U    S1U   EC              EU          OC                OU
2304.0    2304.0   0.0     0.0     19008.0   4186.7   283584.0     0.0

Only the Eden part of heap size was occupied and the current utilization was just 4186.7 KB (EU column). The size of heap usage wasn’t large enough to trigger any GC activities it wasn’t even large enough for a minor collection in new generation. It was safe to say that the OOM generated with test size of 6300 wasn’t the result of exhausted java heap

While the java process with ID 2936 was still running, I could see in Window Task Manager that the virtual memory of the process was almost 1.9 GB. I guess the OOM from the test size of 6300 is about the memory limit of 32bit process which practically around 2 GB (the rest 2 GB is reserved for Window).

Clean it up as soon as possible

I found a very informative link described this GC behavior. One of SUN engineers has clarified that:

We’re throwing an OOM not because the java heap is (anywhere close to) full,but because the non-java (i.e., C/C++) heap is full: you can get an OOM when either heap is full. The reason for the OOM is because unreachable, but not yet finalized, java objects are holding pointers to C/C++ heap memory, and that memory is deallocated pnly when the finalizers run. The JLS doesn’t specify when finalizers get run, so if the VM delays executing them long enough, the C/C++ heap memory doesn’t get freed soon enough to prevent an OOM

An easy fix for the problem is to clean up the Deflater objects as soon as possible by calling deflater.end() method once the compression is done. The modified version of the program can run with test size of 10000 or more. The interesting thing is that with the test size of 10000, the heap utilization is around 6.4 MB which is still not enough for a minor GC collection so all 10000 Deflater objects are still there in heap but the virtual memory of the Java process is just around 331 MB. It seems like the end() method has cut the link between the a Deflater object and the space it has allocated in native memory

Note: HeapDumpOnOutOfMemoryError

I have tried using -XX:+HeapDumpOnOutOfMemoryError JVM options to get heap dump right at the point of OOM but it doesn’t work so I need to lower the test size a bit to keep the process alive and monitor the process using jstat instead. I found an explanation saying that:

Allocation of a native thread is not from the heap or the permanent generation – hence failure to allocate one does not result in a heap dump. (There would be little point in dumping the heap as that is not what was exhausted.)

The OOM caused by native memory usage will not trigger the JVM option

read more

Related Posts

Share This