Google+ Followers

Sunday, January 8, 2012

Page Object Pattern + WebDriver + Spring

During the last week I had start creating a test suite for a existing web-application which my team is currently working on. So now is good time to share some experience:


The application
A normal old java web application: Java 1.4, Struts 1.1, EJB 2.1, JDBC to persist the data and the anemic design (VO+BO+DAO) but I will talk about in another post...


Page Object Pattern
To map the web page's components inside the test code I used the page object pattern.
This patterns helped me to eliminate duplicated code to access web-page's components.

Basically it consists of classes that maps web-pages, so that if you want to fill a login form from your test classes instead of doing this:

  @Test  
  public void someTest() throws Exception {  
      WebElement userNameField = driver.findElement(By.name("j_username"));  
      userNameField.sendKeys(username);  
      WebElement passwordField = driver.findElement(By.name("j_password"));  
      passwordField.sendKeys(password);  
      WebElement logonButon = driver.findElement(By.xpath("/html/body/center/table/tbody/tr[2]/td/form/input"));  
      //asserts  
  }

You do this:

  
  @Test  
  public void someTest()throws Exception {  
      loginPage.open();  
      loginPage.logon(properties.getUsername(), properties.getPassword());  
      //asserts  
  }  

And inside your LoginPage you have:

   public void logon(String username, String password){   
     String username = readUsernameFromFixture();  
     String password = readpasswordFromFixture();  
     WebElement userNameField = driver.findElement(By.name("j_username"));   
     userNameField.sendKeys(username);   
     WebElement passwordField = driver.findElement(By.name("j_password"));   
     passwordField.sendKeys(password);   
     WebElement logonButon = driver.findElement(By.xpath("/html/body/center/table/tbody/tr[2]/td/form/input"));   
     logonButon.click();   
   }   

One thing that I would like to mention here: Keep your assertions in the test class. The idea of the PageObject class is to keep the maps to the web-page's components and to have some helper methods like the example above, not to have assertions.

Wire things togheter.

So now you will have your test class depending on many of the PageObjects, and each PageObject classes also depends on WebDriver:

IMHO is really boring to wire the classes manually, so there comes in Spring.
Spring is very helpful for tests, it is worthy to check the spring documentation about tests.
With simple annotations and just one piece of XML you can wire the pieces together. Check how will be the annotated sample test class:

package br.com.bright.halfprehalfpospaidapplication.test.online;  
...  
imports...  
...  
@ContextConfiguration(locations = "/applicationContext.xml")  
@RunWith(SpringJUnit4ClassRunner.class)  
public class ExpireNoUsedCreditsFunctionalTest {  
    @Autowired  
    private LoginPage loginPage;  
    @Autowired  
    private HomePage homePage;  
    @Autowired  
    private ViewMobilePage viewMobilePage;  
    @Autowired  
    private ViewMobileResultPage viewMobileResultPage;  
    @Autowired  
    private AuxiliaryClassToCheckDataBase akaDaoOrRepository;  
    @Autowired  
    private MenuPage menuPage;  
    @Autowired  
    private Properties properties;  
    private Integer statusBeforeTest;  
    private Integer codeUnderTest;  
    @Before  
    public void setUp() {  
        statusBeforeTest = null;  
        codeUnderTest = null;  
    }  
    @After  
    public void tearDown() {  
        if (statusBeforeTest != null) {  
            akaDaoOrRepository.updateStatusByCode(statusBeforeTest,  
                    codeUnderTest);  
        }  
        menuPage.Logoff();  
    }  
    @Test  
    public void noCreditsToRevert() throws Exception {  
...  

Yes my friends, you can inject dependencies inside private attributes! :O
Your PageObject classes have to be Spring Bean as well, so that Spring will be able to inject them in your test class:

package br.com.bright.thesameapplication.test.online.page;  
import org.openqa.selenium.By;  
import org.openqa.selenium.WebDriver;  
import org.openqa.selenium.WebElement;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  
@Component  
public class LoginPage extends PageBase{  
    @Autowired  
     public LoginPage(WebDriver webDriver) {  
         super(webDriver);  
     }  
     public void logon(String username, String password){  
         WebElement userNameField = driver.findElement(By.name("j_username"));  
         userNameField.sendKeys(username);  
         WebElement passwordField = driver.findElement(By.name("j_password"));  
         passwordField.sendKeys(password);  
         WebElement logonButon = driver.findElement(By.xpath("/html/body/center/table/tbody/tr[2]/td/form/input"));  
         logonButon.click();  
     }  
}  
And in order to inject the WebDriver in the constructor as a dependency we need it to be a Spring Bean as well, but you cannot annotated it as we just downloaded the WebDriver's jar. To solve that we will use the xml piece. Check this out:


 
 
 
  
  
  
  
 

 
  
  
  
  
 
 
 
  
  
  
  
 
 
 



Next Steps...
Normally when we are coding a test suite for a application we end up with a DSL for the application. Probably I will end up with a parent class with some useful method to interact with the application.
Also as I write this post I found a useful information about a page factory built-in WebDriver's support library. It seens to be very helpful.
See you later.

3 comments:

  1. hi Roger,

    did you removed xml from above blog intentionally or it is not visible because of some other reasons?
    It will be helpful if you can add it back.

    ReplyDelete
  2. Bro Roger, do you have the project for download for the above example. I need to understand Spring based Webdriver project as one of my client has an implementation. I had worked extensively on Webdriver + Core Java. I need you help if you can share a sample project that uses spring to run the tests.I'm running out of time :( It would be great,helo from your side. I can easily understand Java project.

    ReplyDelete