2016-10-07 14 views
3

私はページオブジェクト@FindByアノテーションでハードコードされた値しか使用できません しかし、私はロケータを動的に解決したいと思います。セレンページオブジェクト。 @FindByロケータを外部ソースから読み取るには?

public class LoginPage extends BasePage { 

    // hardocded value works ok 
    @FindBy(name = "login field") 
    WebElement usernameFld; 

    // does not compile 
    // this is a kind of what I would like to have 
    @FindBy(getLocatorFromExternalSource()) 
    WebElement passwordFld; 

} 

私はそのようなことは、カスタム注釈/デコレータ/工場を実装することで解決することができるが、まだ例が見つからなかったことを言及するいくつかの記事を見てきました。

QUESTION: 誰かがロケータを動的に解決することができるようカスタムElementLocatorFactoryを実装する方法の一例をお願いできますか?

は、私は同じように昔ながらのスタイルの呼び出しを使用することができます知っている:

driver.findElement(getLocatorFromExternalSource("passwordFld")).click() 

が、私が代わりに

passwordFld.click() 

を使用したいと思います。

+0

あなたのロケータは多くのことを変更しているページ・クラス内でこれらのアクションを実行することができますか?なぜあなたは簡単に見つけて整理することができるあなたのページオブジェクトにそれらを置くのではなく、外部ファイルからそれらを読みたいのですか? – JeffC

+0

1. iosおよびAndroidデバイスのプラットフォームに依存しないテストを作成しようとしており、ページオブジェクトを複製したくないです。 2.外部化されたロケータが有用な次のポイントは、 です。3.ロケータの中には、 (別の注文でソートされた要素など) – ludenus

答えて

0

注釈はMetaDataなので、クラスの読み込み中に実行時に最初に利用できるようにする必要があります。あなたが求めているのはまっすぐな答えではありませんが、ハックはリフレクションを使用しています。IMHOは避けています。ロケータを外部化する必要がある場合は、実行時にランタイムを読み込んでオブジェクトレポジトリを実装することをお勧めしますいくつかの外部ソース。

0

私は個人的にはPageFactoryの大ファンではありませんが、既に使用している場合は、以下のようにします。

public class LoginPage extends BasePage 
{ 
    WebDriver driver; 
    WebElement usernameFld; 
    @FindBy(name = "login field") 
    WebElement usernameIOs; 

    @FindBy(name = "something else") 
    WebElement usernameAndroid; 

    public LoginPage(WebDriver webDriver, Sites site) 
    { 
     this.driver = webDriver; 
     switch (site) 
     { 
      case IOS: 
       usernameFld = usernameIOs; 
       break; 
      case ANDROID: 
       usernameFld = usernameAndroid; 
       break; 
     } 
    } 

    public void setUsername(String username) 
    { 
     usernameFld.sendKeys(username); 
    } 
} 

他の場所で私はロケータを宣言してから、私はそれらを必要としての要素をこすりすることを好む

public enum Sites 
{ 
    IOS, ANDROID 
} 

を定義します。私は、これは多くのパフォーマンスであることを見つけて、あなたは私がここにいくつかの手がかりを見つけた

public class LoginPage extends BasePage 
{ 
    WebDriver driver; 
    By usernameLocator; 
    By usernameIOsLocator = By.name("login field"); 
    By usernameAndroidLocator = By.name("something else"); 

    public LoginPage(WebDriver webDriver, Sites site) 
    { 
     this.driver = webDriver; 
     switch (site) 
     { 
      case IOS: 
       usernameLocator = usernameIOsLocator; 
       break; 
      case ANDROID: 
       usernameLocator = usernameAndroidLocator; 
       break; 
     } 
    } 

    public void setUsername(String username) 
    { 
     driver.findElement(usernameLocator).sendKeys(username); 
    } 
} 
+0

答えをありがとう。私はあなたの考えを持っていると思う。利点は、明示的にロケータを選択することですべてを明確にすることです。欠点は、IOS9、IOS10、iPadといった新しいプラットフォームを追加した後で、すべてのページオブジェクトのすべてのスイッチケースを変更する必要があることです。私はページの設定を変更して、コードをそのままにしておくことを好みます。 – ludenus

+0

あなたがやっていることとはそれほど違いはありません。新しい設定を追加するときは、設定ファイルの各ページの各要素の新しいロケータを追加する必要があります。私はここで同じことをしなければならないだろう。違いは、私の方法は、ページオブジェクトメソドロジをきれいに保ち、そのページと関係するすべてがページオブジェクトにあることです。設定ファイルには、すべての要素のすべてのロケータとサポートするconfigの数を掛け合わせているので、扱いにくいものになります。 – JeffC

+0

私は同意します - ロケータの量は同じです。しかし、私はテストデータをコードと区別することを好む。 – ludenus

0

など、少数の古い要素の問題を得る:https://stackoverflow.com/a/3987430/1073584 をし、最終的に私は3つのクラスを実装することで、欲しかったものを手に入れるの管理: CustomPageFactory、CustomElementLocator、CustomAnnotations。

今、私は設定 をタイプセーフと

//オブジェクトのページを初期化するために、次のコードを使用するように私のロケータを外部化することができる午前======= LoginPage

public class LoginPage extends AbstractPage { 
    Config config; 

    @FindBy(using = "CONFIG") // take locator from config 
    WebElement passwordFld; 

    // .. other fields skipped 


    public LoginPage(WebDriver webDriver, Config config) throws IOException { 
     super(webDriver); 
     this.config = config; 
     PageFactory.initElements(new CustomPageFactory(webDriver, config), this); 
    } 

} 

// == === login.page.typesafe。設定:

passwordFld = { 
    name = "password field" 
} 

// ============= CustomPageFactory

package com.company.pages.support; 

import com.typesafe.config.Config; 
import org.openqa.selenium.WebDriver; 
import org.openqa.selenium.support.pagefactory.ElementLocator; 
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; 

import java.lang.reflect.Field; 

public class CustomPageFactory implements ElementLocatorFactory { 
    private Config config; 
    private WebDriver driver; 

    public CustomPageFactory(WebDriver driver, Config config) { 
     this.driver = driver; 
     this.config = config; 
    } 

    public ElementLocator createLocator(Field field) { 
     return new CustomElementLocator(driver, field, config); 
    } 
} 

// ================ = CustomElementLocator

package com.company.pages.support; 

import com.typesafe.config.Config; 
import org.openqa.selenium.SearchContext; 
import org.openqa.selenium.support.pagefactory.DefaultElementLocator; 

import java.lang.reflect.Field; 

public class CustomElementLocator extends DefaultElementLocator { 

    private Config config; 

    public CustomElementLocator(SearchContext searchContext, Field field, Config config) { 
     super(searchContext, new CustomAnnotations(field, config)); 
     this.config = config; 
    } 


} 

// ====== CustomAnnotations

package com.company.pages.support; 

import com.typesafe.config.Config; 
import com.typesafe.config.ConfigObject; 
import org.openqa.selenium.By; 
import org.openqa.selenium.support.FindBy; 
import org.openqa.selenium.support.pagefactory.Annotations; 

import java.lang.reflect.Field; 

public class CustomAnnotations extends Annotations { 

    Config config; 

    public CustomAnnotations(Field field, Config config) { 
     super(field); 
     this.config = config; 
    } 

    @Override 
    protected By buildByFromShortFindBy(FindBy findBy) { 

     if (findBy.using().equals("CONFIG")) { 

      if (null != config) { 

       ConfigObject fieldLocators = config.getObject(getField().getName()); 

       if (fieldLocators.keySet().contains("className")) 
        return By.className(fieldLocators.get("className").unwrapped().toString()); 

       if (fieldLocators.keySet().contains("css")) 
        return By.cssSelector(fieldLocators.get("css").unwrapped().toString()); 

       if (fieldLocators.keySet().contains("id")) 
        return By.id(fieldLocators.get("id").unwrapped().toString()); 

       if (fieldLocators.keySet().contains("linkText")) 
        return By.linkText(fieldLocators.get("linkText").unwrapped().toString()); 

       if (fieldLocators.keySet().contains("name")) 
        return By.name(fieldLocators.get("name").unwrapped().toString()); 

       if (fieldLocators.keySet().contains("partialLinkText")) 
        return By.partialLinkText(fieldLocators.get("partialLinkText").unwrapped().toString()); 

       if (fieldLocators.keySet().contains("tagName")) 
        return By.tagName(fieldLocators.get("tagName").unwrapped().toString()); 

       if (fieldLocators.keySet().contains("xpath")) 
        return By.xpath(fieldLocators.get("xpath").unwrapped().toString()); 
      } 

     } 

     return super.buildByFromShortFindBy(findBy); 
    } 

} 
+1

ご協力いただきありがとうございますが、このソリューションはselenium v​​2.53にも適用されます。私はセレン - javaによって3.5.3にアップグレードして、メソッドbuildByFromShortFindByがAnnotationsクラスに属しているのを見ることができなくなったので、AbstractFindByBuilderに移動して、この関数をCustomPageFactoryを実装するために配線&オーバーライドする方法を知りましたか? –

+0

@BhuvaneshManiあなたは正しいです、このコードは新しいバージョンのセレンでは動作しません – ludenus

0

私はあなたが実行するように不思議に思っていると思います自分の名前

によってwebElements上の操作は、Uは、あなたが他のクラス からドライバを受け入れている場合ではない場合がthis.driver削除する場合は上記のコード

public AnyConstructorName(AndroidDriver<AndroidElement> driver) { 
     this.driver =driver; 
     PageFactory.initElements(driver, this); 
} 

が動作するあなたのコンストラクタでいくつかのコードを配置する必要があり=あなたのコンストラクタ

からドライバが

@FindBy(xpath ="//android.widget.TextView[@text='Payments']") 

WebElement pay; 

uは(pay.clickを行うことができます)などのWeb要素を初期化します。

または任意の操作は、あなたは

をしたいしかし、唯一の

+0

'@FindBy(xpath =" // android.widget.TextView [@ text = 'Payments'] ")' 私は道を探していました@FindBy内のロケータをハードコードするのではなく、外部のソースから読み込む – ludenus

関連する問題