Selenium Webdriver 的 PageObject 改造

sxazf · 2017年11月10日 · 最后由 sxazf 回复于 2017年11月12日 · 1274 次阅读
本帖已被设为精华帖!

PageObject中提供了一个@FindBy注解,也非常好用,但由于其是一次性全部初始化所有的WebElement,对于当前还不存在于页面上的Element在初始化时就会报错,为了解决这个问题,自然就不能用这个@FindBy注解了,而我们可以自已去写一个类似的注解来解决这个问题,下面是思路的实现:

自定义一个@FindBy注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface FindBy {

String name();

int age();

int lazy() default 0;
}

定义一个TestInfo类来模拟WebElement类

public class TestInfo {

private String name;

private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "TestInfo [name=" + name + ", age=" + age + "]";
}

}

解析@FindBy注解

public class PageUtil {

private static void initialLazyInfo(Object obj, int lazy) {
try{
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(FindBy.class) && field.getType().equals(TestInfo.class)){
FindBy findBy = field.getAnnotation(FindBy.class);
if(findBy.lazy()==lazy){
TestInfo temp = new TestInfo();
temp.setName(findBy.name());
temp.setAge(findBy.age());
field.setAccessible(true);
field.set(obj, temp);
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}

public static void initialLazy(Object obj){
PageUtil.initialLazyInfo(obj, 0);
}

public static void initialLazy(Object obj, int lazy){
PageUtil.initialLazyInfo(obj, lazy);
}

}

使用方式

public class DemoPage {

public DemoPage() {
PageUtil.initialLazy(this);
}

@FindBy(name="zf1", age=1)
private TestInfo info1;

@FindBy(name="zf2", age=2, lazy=1)
private TestInfo info2;

public void test(){
System.out.println("info1 is: " + info1);
System.out.println("info2 is: " + info2);
PageUtil.initialLazy(this, 1);
System.out.println("info2 is: " + info2);
}

public static void main(String[] args) {
DemoPage dp = new DemoPage();
dp.test();
}

}

运行结果

info1 is: TestInfo [name=zf1, age=1]
info2 is: null
info2 is: TestInfo [name=zf2, age=2]

说明

  • 将TestInfo初始化进行了分层次的初始化
  • 在需要用到的地方用PageUtil.initialLazy(this, int);进行初始化

以上虽然实现了分层次的初始化,但是在要用到的地方都需要调用PageUtil.initialLazy(this, int);,还是显得有点麻烦,要解决这个问题,还得从改造WebElement类开始,然后自定义一个@FindBy注解,并且在需要用到元素的地方再去判断并初始化。

定义@FindBy注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface FindBy {

String id() default "";

String name() default "";

String className() default "";

String css() default "";

String tagName() default "";

String linkText() default "";

String partialLinkText() default "";

String xpath() default "";
}

@FindBy注解中的值结赋到一个中间类中去,在Page类进行初始化的时候进行赋值

public class FindElement {

private String id;

private String name;

private String className;

private String css;

private String tagName;

private String linkText;

private String partialLinkText;

private String xpath;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getClassName() {
return className;
}

public void setClassName(String className) {
this.className = className;
}

public String getCss() {
return css;
}

public void setCss(String css) {
this.css = css;
}

public String getTagName() {
return tagName;
}

public void setTagName(String tagName) {
this.tagName = tagName;
}

public String getLinkText() {
return linkText;
}

public void setLinkText(String linkText) {
this.linkText = linkText;
}

public String getPartialLinkText() {
return partialLinkText;
}

public void setPartialLinkText(String partialLinkText) {
this.partialLinkText = partialLinkText;
}

public String getXpath() {
return xpath;
}

public void setXpath(String xpath) {
this.xpath = xpath;
}

}

Page类实例化时初始化@FindBy注解并赋值给FindElement类

public class PageUtil {

public static void initialWebElement(Object obj) {
try{
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(FindBy.class) && field.getType().equals(WebElement.class)){
FindBy findBy = field.getAnnotation(FindBy.class);
FindElement findElement = new FindElement();
if(!"".equals(findBy.id())){
findElement.setId(findBy.id());
}else if(!"".equals(findBy.name())){
findElement.setName(findBy.name());
}else if(!"".equals(findBy.className())){
findElement.setClassName(findBy.className());
}else if(!"".equals(findBy.css())){
findElement.setCss(findBy.css());
}else if(!"".equals(findBy.tagName())){
findElement.setTagName(findBy.tagName());
}else if(!"".equals(findBy.linkText())){
findElement.setLinkText(findBy.linkText());
}else if(!"".equals(findBy.partialLinkText())){
findElement.setPartialLinkText(findBy.partialLinkText());
}else if(!"".equals(findBy.xpath())){
findElement.setXpath(findBy.xpath());
}
WebElementExt ext = new WebElementExt(findElement);
field.setAccessible(true);
field.set(obj, ext);
}
}
}catch(Exception e){
e.printStackTrace();
}
}

}

改造WebElement类,利用的是装饰器模式

public class WebElementExt implements WebElement {

private static WebDriver driver = new FirefoxDriver();

private FindElement findElement;

public WebElementExt(FindElement findElement) {
this.findElement = findElement;
}

private WebElement element;

private WebElement getWebElement(){
if(element != null){
return element;
}
if(findElement.getId() != null){
element = this.waitForElement(By.id(findElement.getId()));
}else if(findElement.getName() != null){
element = this.waitForElement(By.name(findElement.getName()));
}else if(findElement.getClassName() != null){
element = this.waitForElement(By.className(findElement.getClassName()));
}else if(findElement.getCss() != null){
element = this.waitForElement(By.cssSelector(findElement.getCss()));
}else if(findElement.getTagName() != null){
element = this.waitForElement(By.tagName(findElement.getTagName()));
}else if(findElement.getLinkText() != null){
element = this.waitForElement(By.linkText(findElement.getLinkText()));
}else if(findElement.getPartialLinkText() != null){
element = this.waitForElement(By.partialLinkText(findElement.getPartialLinkText()));
}else if(findElement.getXpath() != null){
element = this.waitForElement(By.xpath(findElement.getXpath()));
}
if(this.waitElementToBeDisplayed(element)){
return element;
}
return null;
}

private WebElement waitForElement(final By by) {
WebElement element = null;
try {
element = new WebDriverWait(driver, 20).until(new ExpectedCondition<WebElement>() {
public WebElement apply(WebDriver d) {
return d.findElement(by);
}
});
} catch (Exception e) {
e.printStackTrace();
}
return element;
}

private boolean waitElementToBeDisplayed(final WebElement element) {
boolean wait = false;
if (element == null)
return wait;
try {
wait = new WebDriverWait(driver, 20).until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return element.isDisplayed();
}
});
} catch (Exception e) {
e.printStackTrace();
}
return wait;
}

@Override
public void click() {
this.getWebElement();
element.click();
}

@Override
public void submit() {
this.getWebElement();
element.submit();
}

@Override
public void sendKeys(CharSequence... charSequences) {
this.getWebElement();
element.sendKeys(charSequences);
}

@Override
public void clear() {
this.getWebElement();
element.clear();
}

@Override
public String getTagName() {
this.getWebElement();
return element.getTagName();
}

@Override
public String getAttribute(String s) {
this.getWebElement();
return element.getAttribute(s);
}

@Override
public boolean isSelected() {
this.getWebElement();
return element.isSelected();
}

@Override
public boolean isEnabled() {
this.getWebElement();
return element.isEnabled();
}

@Override
public String getText() {
this.getWebElement();
return element.getText();
}

@Override
public List<WebElement> findElements(By by) {
this.getWebElement();
return element.findElements(by);
}

@Override
public WebElement findElement(By by) {
this.getWebElement();
return element.findElement(by);
}

@Override
public boolean isDisplayed() {
this.getWebElement();
return element.isDisplayed();
}

@Override
public Point getLocation() {
this.getWebElement();
return element.getLocation();
}

@Override
public Dimension getSize() {
this.getWebElement();
return element.getSize();
}

@Override
public String getCssValue(String s) {
this.getWebElement();
return element.getCssValue(s);
}
}

使用方法

public class DemoPage {

public DemoPage() {
PageUtil.initialWebElement(this);
}

@FindBy(xpath="//abc")
private WebElement element;

public void clieckButton(){
element.click();
}

}

经过上面这样改造后,即便是页面中动态加载的元素,也不用担心会报错了,也不用多次初始化了!

共收到 5 条回复 时间 点赞
seveniruby 将本帖设为了精华贴 11月11日 22:13

有创新, 请在你的代码块中注明语法. markdown语法支持语言标注.

有点像selenium的 PageFactor

学习了!想法不错,写的也通俗易懂,赞一个!

MeYoung 回复

就是根据的pagefactory进行的改造!

vieira 回复

多谢支持!

seveniruby 线下班第二期_兼容性测试_20180318 中提及了此贴 03月18日 15:20
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册