Page Object Pattern

· Read in about 1 min · (129 Words)

移动UI自动化,看起来美好,践行起来却难。做个目光短见的实主义者。Page Objects Pattern是Selenium官方推崇的方式,最近研究写测试用例最佳实践之Page Objects,同时结合Appium的Java Client简单介绍下如何写出靠谱的Page Object。

Page Object定义为抽象web app页面的一系列对象,通过对UI界面的抽象,它拥有很多好处:

  • 减少重复代码
  • 提高测试代码的可读性和稳定性
  • 测试代码易于维护
  • UI元素的定位和具体实现分离,如Android,iOS的一套脚本实现

一个简单的例子

public class BaiduSearchPage {

	protected WebDriver driver;
	@FindBy(id="kw")
	private WebElement kw;
	private WebElement su;

	public BaiduSearchPage(WebDriver driver) {
		super();
		this.driver = driver;
    PageFactory.initElements(driver, this);
	}
	public void load(String url) {
		driver.get(url);
	}
	public ResultPage search(String key) {
		kw.clear();
		kw.sendKeys(key);
		su.click();
		return new ResultPage(driver);
	}
}

推荐的做法

  • public方法暴露Page对象的服务
  • WebElement,Driver相关页面UI细节尽可能隐藏
  • 尽量减少Page对象中的Assertion
  • 在方法中返回新的Page,甚至在同一页面也可以返回Page做链式操作
  • 一个Page对象不需要关注所有细节,只关心需要的UI元素,需要时再补充
  • 不同的结果,同一个操作可以用不同的方法。

Appium 中使用Page Object Pattern

Appium的Java Client是基于WebDriver的,但有了一些改进。比如元素定位不到时,它会将Locator详细信息抛出,而Selenium没有。

Wait

移动自动化测试Wait是很关键的一个动作,既关乎正确性,也关乎效率,我们应该极力避免使用Thread.sleep()或Sleeper.sleepTight()。Appium的客户端提供了一个类AppiumFieldDecorator可以很方便的设置ImplicitlyWaitTimeOut。FieldDecorator顾名思义,是Page对象Field的Decorator,PageObject的精髓就是在Feild上下功夫,将WebElement类型的Feild动态Proxy为一个增强的WebElement,这个成员在每次操作时,都会先使用注解的定位策略定位,然后再调用WebElement的方法,当然可以通过CacheLookup注解,来缓存定位结果。

PageFactory.initElements(new AppiumFieldDecorator(driver, 5, TimeUnit.SECONDS), pageObject);

如果等待某个页面元素是否可见,在PageObject中也更简单

public static void untilElementVisable(final WebElement element,int timeoutInSeconds){
  new Wait() {
     @Override
     public boolean until() {
		return element.isDisplayed();
     }
   }.wait(String.format("Timed out waiting for %s. Waited %s",
		  element, timeoutInSeconds), timeoutInSeconds);
}

FindBy

在Appium中你会遇到,Selendroid模式和UIAutomator定位差异,比如Selendroid的linkText在UIAutomator中用name,还有就是iOS脚本想和Android共用一份。这在Appium中有了很好的扩充,Appium客户端会在运行时决定使用哪个Annotation来装饰WebElement。

@FindBy(name="text")
@SelendroidFindBy(name = "text1")
@iOSFindBy(id="sth")
private WebElement textSelendroid;

ElementInterceptor

总是有这样或那样的原因,需要记录日志,如果方法的执行每一步都要手写是很痛苦的,自然我们想到了AOP。在Selenium中EventFiringWebDriver类可以方便的记录日志,但是在Appium客户端中,我们可以修改AppiumFieldDecorator中ElementInterceptor来加入自己的日志信息,不过暂时这个功能Appium Client没有暴露出来,需要自己fork个repo修改下。

参考

Comments