51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2370|回复: 1
打印 上一主题 下一主题

webdriver脚本设计 Loadable Component 模式介绍

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2012-9-24 13:31:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 br1823 于 2012-9-24 13:36 编辑

这里简单的介绍一下使用Loadable Component 的使用方法,后面附上selenium官方wiki中的详细介绍。

欢迎大家加入  selenium讨论 QQ群 237754909

使用Loadable Component编写PageObjects方式的webdriver脚本会非常的方便。

第一步
当你编写一个page类时,首先需要继承Loadable Component 这个父类,方式如下:

  1. public class TestPage extends LoadableComponent<TestPage> {
  2. //......
  3. }
复制代码



第二步
当你继承Loadable Component  后需要实现父类的两个方法:



  1. @Override
  2.   protected void load() {
  3.     driver.get("http://xxxxxxxxxxx");
  4.   }

  5.   @Override
  6.   protected void isLoaded() throws Error {
  7.     String url = driver.getCurrentUrl();
  8.     assertTrue("Not on the issue entry page: " + url, url.endsWith("xxxxxx"));
  9.   }
复制代码


第三步
定义改page类的相关页面元素和方法,这里就是Loadable Component的方便之处。方法如下:
  1. // By default the PageFactory will locate elements with the same name or id
  2.   // as the field. Since the summary element has a name attribute of "summary"
  3.   // we don't need any additional annotations.
  4.   private WebElement summary;
  5.   
  6.   // Same with the submit element, which has the ID "submit"
  7.   private WebElement submit;
复制代码


summary  submit 是我们定义的页面元素,在使用时,Loadable Component 的实现会自动的使用PageFactory的方法在页面中查找 name或id 为summary  submit  的元素。

如果我们不想使用与页面元素相同名字的变量,可以通过PageFcatory的@FindBy方法重写元素定义,方式如下:
@FindBy(name = "XXXXXXX")

第四步
在page类中实现PageFcatory,写一个构造函数  方法如下:

  1. public
  2. TestPage  (WebDriver driver) {
  3.     this.driver = driver;
  4.    
  5.     // This call sets the WebElement fields.
  6.     PageFactory.initElements(driver, this);
  7.   }
复制代码



这样就简单的实现了Loadable Component

第五步
测试脚本的编写与PageFcatory 基本一样。
由于每个页面的url已经在page类中写好,使用时只需要在new这个page类时后面跟上get 方法即可打开url,方法如下:



  1. TestPage  page = new
  2. TestPage(driver).get();
复制代码



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

该用户从未签到

2#
 楼主| 发表于 2012-9-24 13:32:04 | 只看该作者
以下是官方wiki中的详细介绍:

Using the LoadableComponent

What Is It?
The LoadableComponent is a base class that aims to make writing PageObjects less painful. It does this by providing a standard way of ensuring that pages are loaded and providing hooks to make debugging the failure of a page to load easier. You can use it to help reduce the amount of boilerplate code in your tests, which in turn make maintaining your tests less tiresome.
There is currently an implementation in Java that ships as part of Selenium 2, but the approach used is simple enough to be implemented in any language.


Simple Usage
As an example of a UI that we'd like to model, take a look at the new issue page. From the point of view of a test author, this offers the service of being able to file a new issue. A basic Page Object would look like:
package com.example.webdriver;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

publicclassEditIssue{

privatefinalWebDriver driver;

publicEditIssue(WebDriver driver){
this.driver = driver;
}

publicvoid setSummary(String summary){
WebElement field = driver.findElement(By.name("summary"));
    clearAndType(field, summary);
}

publicvoid enterDescription(String description){
WebElement field = driver.findElement(By.name("comment"));
    clearAndType(field, description);
}

publicIssueList submit(){
    driver.findElement(By.id("submit")).click();
returnnewIssueList(driver);
}

privatevoid clearAndType(WebElement field,String text){
    field.clear();
    field.sendKeys(text);
}
}
In order to turn this into a LoadableComponent, all we need to do is to set that as the base type:
public class EditIssue extends LoadableComponent<EditIssue> {
  // rest of class ignored for now
}
This signature looks a little unusual, but it all it means is that this class represents a LoadableComponent that loads the EditIssue page.
By extending this base class, we need to implement two new methods:
@Override
  protected void load() {
    driver.get("http://code.google.com/p/selenium/issues/entry");
  }

  @Override
  protected void isLoaded() throws Error {
    String url = driver.getCurrentUrl();
    assertTrue("Not on the issue entry page: " + url, url.endsWith("/entry"));
  }
The load method is used to navigate to the page, whilst the isLoaded method is used to determine whether we are on the right page. Although the method looks like it should return a boolean, instead it performs a series of assertions using JUnit's Assert class. There can be as few or as many assertions as you like. By using these assertions it's possible to give users of the class clear information that can be used to debug tests.
With a little rework, our PageObject looks like:
package com.example.webdriver;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import static junit.framework.Assert.assertTrue;

public class EditIssue extends LoadableComponent<EditIssue> {

  private final WebDriver driver;
  
  // By default the PageFactory will locate elements with the same name or id
  // as the field. Since the summary element has a name attribute of "summary"
  // we don't need any additional annotations.
  private WebElement summary;
  
  // Same with the submit element, which has the ID "submit"
  private WebElement submit;
  
  // But we'd prefer a different name in our code than "comment", so we use the
  // FindBy annotation to tell the PageFactory how to locate the element.
  @FindBy(name = "comment") private WebElement description;
  
  public EditIssue(WebDriver driver) {
    this.driver = driver;
   
    // This call sets the WebElement fields.
    PageFactory.initElements(driver, this);
  }

  @Override
  protected void load() {
    driver.get("http://code.google.com/p/selenium/issues/entry");
  }

  @Override
  protected void isLoaded() throws Error {
    String url = driver.getCurrentUrl();
    assertTrue("Not on the issue entry page: " + url, url.endsWith("/entry"));
  }
  
  public void setSummary(String issueSummary) {
    clearAndType(summary, issueSummary);
  }

  public void enterDescription(String issueDescription) {
    clearAndType(description, issueDescription);
  }

  public IssueList submit() {
    submit.click();
    return new IssueList(driver);
  }

  private void clearAndType(WebElement field, String text) {
    field.clear();
    field.sendKeys(text);
  }
}
That doesn't seem to have bought us much, right? One thing it has done is encapsulate the information about how to navigate to the page into the page itself, meaning that this information's not scattered through the code base. It also means that we can do this in our tests:
EditIssue page = new EditIssue(driver).get();
This call will cause the driver to navigate to the page if that's necessary.

Advanced Usage: Nested Components
LoadableComponents start to become more useful when they are used in conjunction with other LoadableComponents. Using our example, we could view the "edit issue" page as a component within a project's website (after all, we access it via a tab on that site). You also need to be logged in to file an issue. We could model this as a tree of nested components:
+ ProjectPage
+---+ SecuredPage
     +---+ EditIssue
What would this look like in code? For a start, each logical component would have its own class. The "load" method in each of them would "get" the parent. The end result, in addition to the EditIssue class above is:
ProjectPage.java:
package com.example.webdriver;

import org.openqa.selenium.WebDriver;

import static org.junit.Assert.assertTrue;

public class ProjectPage extends LoadableComponent<ProjectPage> {

  private final WebDriver driver;
  private final String projectName;

  public ProjectPage(WebDriver driver, String projectName) {
    this.driver = driver;
    this.projectName = projectName;
  }

  @Override
  protected void load() {
    driver.get("http://" + projectName + ".googlecode.com/");
  }

  @Override
  protected void isLoaded() throws Error {
    String url = driver.getCurrentUrl();

    assertTrue(url.contains(projectName));
  }
}
and SecuredPage.java:
package com.example.webdriver;

import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

import static org.junit.Assert.fail;

public class SecuredPage extends LoadableComponent<SecuredPage> {

  private final WebDriver driver;
  private final LoadableComponent<?> parent;
  private final String username;
  private final String password;

  public SecuredPage(WebDriver driver, LoadableComponent<?> parent, String username, String password) {
    this.driver = driver;
    this.parent = parent;
    this.username = username;
    this.password = password;
  }

  @Override
  protected void load() {
    parent.get();

    String originalUrl = driver.getCurrentUrl();

    // Sign in
    driver.get("https://www.google.com/accounts/ServiceLogin?service=code");
    driver.findElement(By.name("Email")).sendKeys(username);
    WebElement passwordField = driver.findElement(By.name("Passwd"));
    passwordField.sendKeys(password);
    passwordField.submit();

    // Now return to the original URL
    driver.get(originalUrl);
  }

  @Override
  protected void isLoaded() throws Error {
    // If you're signed in, you have the option of picking a different login.
    // Let's check for the presence of that.

    try {
      WebElement div = driver.findElement(By.id("multilogin-dropdown"));
    } catch (NoSuchElementException e) {
      fail("Cannot locate user name link");
    }
  }
}
The "load" method in EditIssue now looks like:
@Override
  protected void load() {
    securedPage.get();

    driver.get("http://code.google.com/p/selenium/issues/entry");
  }
This shows that the components are all "nested" within each other. A call to get() in EditIssue will cause all its dependencies to load too. The example usage:
public class FooTest {
  private EditIssue editIssue;

  @Before
  public void prepareComponents() {
    WebDriver driver = new FirefoxDriver();

    ProjectPage project = new ProjectPage(driver, "selenium");
    SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret");
    editIssue = new EditIssue(driver, securedPage);
  }

  @Test
  public void demonstrateNestedLoadableComponents() {
    editIssue.get();

    editIssue.setSummary("Summary");
    editIssue.enterDescription("This is an example");
  }
}
If you're using a library such as Guiceberry in your tests, the preamble of setting up the PageObjects can be omitted leading to nice, clear, readable tests.
回复 支持 反对

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-13 14:01 , Processed in 0.069802 second(s), 29 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表