2016-10-07 1 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; 

} 

나는 그런 일을 사용자 정의 주석/장식/공장을 구현하여 해결 될 수 있지만, 아직 예를 찾지 못했음을 언급하는 몇 가지 게시물을 보았다.

질문 : 누군가가 로케이터 동적으로 해결 될 수 있도록 사용자 정의 ElementLocatorFactory을 구현하는 방법의 을주지하시기 바랍니다 수 있습니까?

는 내가처럼 평범한 구식 스타일의 호출을 사용할 수 있습니다 알고

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

하지만 대신

passwordFld.click() 

를 사용하고 싶습니다.

+0

당신의 로케이터 많이를 변경하는 페이지 클래스 내에서이 작업을 수행 할 수 있습니다? 왜 쉽게 찾을 수 있고 정리되어있는 페이지 개체에 파일을 넣는 대신 외부 파일에서 읽으려고합니까? – JeffC

+0

1. ios 및 Android 장치에 대한 플랫폼 독립적 테스트를 작성하려고하며 페이지 개체를 복제하려고하지 않습니다. 2. 외부화 된 로케이터가 유용 할 다음 지점이 될 수 있습니다. 3. 일부 로케이터는 런타임 중에 변경할 수 있습니다 (다른 순서로 정렬 된 요소들) – ludenus

답변

0

주석은 메타 데이터이므로 클래스를로드하는 동안 런타임에 시작할 때 사용할 수 있어야합니다. 당신이 추구하는 것은 곧장 앞으로 대답을 가지고 있지 않지만 해킹은 Reflections를 사용하고 있습니다. 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

답변 해 주셔서 감사합니다. 나는 네 생각을 가지고 있다고 생각해. 장점은 locator를 명시 적으로 선택하여 모든 것을 명확하게한다는 점입니다. 단점은 IOS9, IOS10, iPad와 같은 새로운 플랫폼을 추가하면 모든 페이지 객체에 대한 모든 스위치 케이스를 수정해야한다는 것입니다. 나는 페이지 구성을 바꾸거나 코드를 그대로 유지하는 것을 선호한다. – ludenus

+0

정말 그렇게 많이 다르지 않습니다. 새 구성을 추가 할 때 구성 파일의 각 페이지에 각 요소에 대한 새 위치 지정자를 추가해야합니다. 나는 여기에서도 똑같이해야 할 것이다. 차이점은 내 방법은 페이지 개체 방법론을 깨끗하게 유지하여 페이지와 관련된 모든 작업이 페이지 개체에 있다는 점입니다. 구성 파일에는 모든 요소에 대한 모든 로케이터 수와 지원하는 구성 수를 포함하고 있기 때문에 다루기가 쉽지 않습니다. – 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 v2.53에 적용 가능합니다. 셀레늄 - 자바 3.5.3으로 업그레이 드하고 메서드를 buildByFromShortFindBy 더 이상 주석 클래스에 속하는 볼 수 없다, 그것은 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; 

유 (pay.click을 수행 할 수 있습니다)와 같은 웹 요소를 초기화;

또는 작동 당신은

을 원하지만

+0

'@FindBy (xpath = "// android.widget.TextView [@ text = 'Payments']")' 나는 길을 찾고있었습니다. @FindBy 내의 로케이터를 하드 코드하지 말고 대신 외부 소스에서 읽습니다. – ludenus

관련 문제