天天看點

Java+Selenium做UI自動化中@FindBy和@CacheLookup用法【多測師_王sir】

一、@FindBy和@CacheLookup用法
代碼執行個體

package page;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class BDPage {
    @FindBy(id="kw")
    @CacheLookup
    public WebElement keyword_input;
    @FindBy(id="su")
    @CacheLookup
    public WebElement search_button;
    public BDPage(WebDriver driver){
        PageFactory.initElements(driver, this); 
    }   
}

元素聲明的寫法,這三行是一個整體:

@FindBy(id="kw")
@CacheLookup
public WebElement keyword_input;

注解: 
@FindBy:這個定義了你所查找的元素是以什麼方式 定位的,比如圖中我用的是id,那麼就寫成 @FindBy(id=”kw”),
還有其他幾種寫法:@FindBy(name=”xx”)、@FindBy(className=”xx”)、@FindBy(xpath=”xxx”)、@FindBy(css=”xxx”)等

@CacheLookup:意思是說找到元素之後将緩存元素,重複的使用這些元素,将使測試的速度大大加快。

WebElement keyword_input:就是變量名

二、PageFactory
    public BDPage(WebDriver driver){
        PageFactory.initElements(driver, this); 
    }

PageFactory是為了支援頁面設計模式而開發出來的,它的方法在selenium.support庫裡面。 
它提供初始化頁面元素的方法,如果頁面存在大量的AJAX的技術,隻要頁面更新一次,它就好重新查找一次元素,是以不
會出現StaleElementException這個error, 
頁面設計模式,可以提供你一個接口,然後你在這個接口上面,建構你自己項目中的頁面對象,使用PageFactory使得測
試更簡單,更少的代碼編寫。 
如果@FindBy沒有指定,它會預設的查找id的屬性,然後是name屬性,如果還沒有找到就會報錯。 如果這個元素存在,
我們不用擔心它, pageFactory會初始化這個元素,不會報任何錯誤。

二、selenium流行的設計模式page object
selenium目前比較流行的設計模式就是page object,那麼到底什麼是page object呢,簡單來說,就是把頁面作為對象,
在使用中傳遞頁面對象,來使用頁面對象中相應的成員或者方法,能更好的提現java的面向對象和封裝特性。而使用時間長了
會發現該模式也存在一點問題,那就是元素每次都要擷取,并且擷取元素與頁面方法不分離,增加代碼備援度,用過springMVC架構
的人都知道,注解方式的開發會大大增加開發效率,使頁面變得整潔。

  本次要介紹的就是pageFactory 設計模式,什麼是pageFactory 設計模式呢?

 準确來說就是在page object模式基礎上更好的利用了面向對象的思維,将擷取元素與操作頁面的方法進行分離,以前擷取
元素要findelementbyid等等,現在隻要一個注解就可以搞定,并且再次跑自動化回歸測試時候,代碼有擷取緩存的特性,
是以會比第一次跑的快,隻要id,name不變。




1.首先介紹FindBy類:

For example, these two annotations point to the same element:

 @FindBy(id = "f") WebElement f;
 @FindBy(how = How.ID, using = "f") WebElement f; 
and these two annotations point to the same list of elements:

 
 @FindBy(tagName = "a") List<WebElement> links;
 @FindBy(how = How.TAG_NAME, using = "a") List<WebElement> links;
用來分别查找單個元素和多個元素的兩種用法,支援的類型有:className、css、id、linkText、name、
partialLinkText、tagName、xpath。

How支援的類型和上面差不多。

2.接着介紹PageFactory類

Factory class to make using Page Objects simpler and easier.

它提供的方法都是靜态的,可以直接調用,我們在用完findby後,需要進行元素初始化,則需要調用下面的方法

initElements(ElementLocatorFactory factory, java.lang.Object page)、initElements(FieldDecorator 
decorator, java.lang.Object page)、initElements(WebDriver driver, java.lang.Class<T> pageClassToProxy)、
initElements(WebDriver driver, java.lang.Object page)

我們在實際使用中可以這樣用:

PageFactory.initElements(dr, XXX.class);
或者

PageFactory.initElements(new AjaxElementLocatorFactory(dr, 10) ,XXX.class);
後者加入了初始化元素時等待時間。

3.下面給大家簡單用個例子介紹我的設計模式。
public class BasePage {
   WebDriver driver;
   private final int  TIMEOUT=3;
   public  BasePage(WebDriver driver )
   {
  this.driver=driver;
  PageFactory.initElements(new AjaxElementLocatorFactory(driver, TIMEOUT) , this);
  
   }
   public BasePage(WebDriver driver,final String title)
   {
  this.driver=driver;
  WebDriverWait wait=new WebDriverWait(driver, TIMEOUT);
  try{
           boolean flag = wait.until(new ExpectedCondition<Boolean>(){
               @Override
               public Boolean apply(WebDriver arg0) {
                   // TODO Auto-generated method stub
                   String acttitle = arg0.getTitle();
                   return acttitle.equals(title);                    
               }});
       }catch(TimeoutException te) {
           throw new IllegalStateException("目前不是預期頁面,目前頁面title是:" + driver.getTitle());
       }
  PageFactory.initElements(new AjaxElementLocatorFactory(driver, TIMEOUT) , this);
   }

這是我的基礎頁面,以後任何頁面都繼承該頁面,因為判斷UI層次的頁面标題。

2.登入頁面

public class LoginPage extends BasePage {
    
@FindBy(id="ele_active_close")
@CacheLookup
private WebElement wd;


@FindBy(id="at_index_login-box_mobile")
@CacheLookup
private WebElement inputusername;

@FindBy(id="at_index_login-box_password")
@CacheLookup
private WebElement inputpassword;

@FindBy(id="at_index_login-box_p_forget_login")
@CacheLookup
private WebElement mybutton;

public LoginPage(WebDriver driver) {
// TODO Auto-generated constructor stub
super(driver);
}


public LoginPage(WebDriver driver, String title) {
super(driver, title);
// TODO Auto-generated constructor stub
}

//不設定homepage類型不行,因為傳回的是這個對象 必須是這個類型
public HomePage login(){
JbClose();
username("");
password("");
mybutton();
return new HomePage(driver,"xxx);
}
public void  username(String username)
{
inputusername.clear();
inputusername.sendKeys(username);
}
public void password(String password)
{
inputpassword.clear();
inputpassword.sendKeys(password);
}
public void mybutton()
{
mybutton.click();
}
public void JbClose()
{
wd.click();
}
}

  3.登入後要進入什麼頁面

public class HomePage  extends BasePage{
   


public HomePage(WebDriver driver) {
super(driver);
// TODO Auto-generated constructor stub
}
public HomePage(WebDriver driver,String title)
{
super(driver,title);
}

     
}

4.下面就是main方法了,實際中建議和testng混合使用

public class TestFactory {
  public static void main(String[] args) {
 
WebDriver driver=new ChromeDriver();
driver.get("http://petrocoke-web-test.obaymax.com/");
String logintitle="xxx";
LoginPage lg=new LoginPage(driver, logintitle);
HomePage hm=lg.login();
System.out.println("測試結束");
driver.quit();
}
}

三、selenium之設計模式POM 

前面介紹了POM的優點和非POM方式寫腳本,這篇介紹利用頁面工廠類(page factory)去實作POM,
通過檢視PageFactory類,我們可以知道它是一個初始化一個頁面執行個體的功能,在執行個體化該頁面對象時候,
也會一起執行個體化該頁面的元素定位。直接來看看京東登入的例子,如果用POM實作,在測試腳本中實際代碼就2行。

1.在pageObjects包建立一個京東首頁類,代碼如下

package pageObects;
 
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
 
public class JdHomePage {
    
    // 元素定位
    //登入連結
    @FindBy (id="ttbar-login")
    WebElement login_link;
    
    //賬戶登入
    @FindBy (xpath="//*/div[@class='login-form']/div[2]/a")
       WebElement login_withAccount;
    
    //輸入使用者名框
    @FindBy (id="loginname")
    WebElement inputBox_username;
    
    //輸入密碼
    @FindBy (id="nloginpwd")
    WebElement inputBox_password;
    
    //登入按鈕
    @FindBy (id="loginsubmit")
    WebElement login_submitBtn;
    
    // 業務邏輯和操作方法
    
    //登入方法
    public void login(String username, String password){
        
        login_link.click();
        login_withAccount.click();
        inputBox_username.sendKeys(username);
        inputBox_password.sendKeys(password);
        login_submitBtn.click();
        
    }
    
 
}
2.在testSuites包下建立一個測試類
package testSuites;
 
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.PageFactory;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
 
import pageObects.JdHomePage;
 
public class TestWithPOM {
WebDriver driver;
    
    @BeforeClass
    public void setUp() throws Exception{
        
        System.setProperty("webdriver.chrome.driver", ".\\Tools\\chromedriver.exe");
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        driver.get("https://www.jd.com/");
        Thread.sleep(2000);
    }
    
 
    @Test
    public void testLogin(){
        
        JdHomePage hp = PageFactory.initElements(driver, JdHomePage.class);
        hp.login("user1", "123456");
    }
    
 
}
       處理打開浏覽器和打開京東首頁操作,實際測試登入的代碼就2行對不對。這裡我們借助了PageFactory類的方法,在初始化一
個頁面類的時候,也會一起把該頁面定義的元素定位也會初始化,然後通過頁面對象調用頁面類的頁面操作方法。這種方式,我們把元素
定位和業務邏輯操作都寫在了頁面對象類中,測試腳本類,直接調用頁面類的方法,這樣的測試腳本看起來很簡潔,友善閱讀和維護,如果
登入相關文案發生變更,我們隻需要去改登入對應頁面的元素定位和業務邏輯,不需要修改這個登入測試類。      

繼續閱讀