您无法使用 PageObjects 和 PageFactory 的普通风味来做到这一点。
您需要进行一些自定义,然后才能完成此操作。
要完成这项工作,您基本上需要以下内容
-
org.openqa.selenium.support.pagefactory.ElementLocator 的自定义实现,以便您可以使用提供的WebDriver 实例调用findElement 和findElements。默认实现只是从注解中查询。
-
org.openqa.selenium.support.pagefactory.ElementLocatorFactory 的自定义实现,以便它可以生成您自定义的 ElementLocator 实例,而不是默认实例。
- 一个自定义注解解析器,以便您可以通过扩展
org.openqa.selenium.support.pagefactory.AbstractAnnotations 来解析您添加到WebElement 对象的自定义注解
- 最后但并非最不重要的一点是,一个自定义注释可捕获一些元数据,这些元数据可用于从给定 Web 元素的 JSON 文件中读取定位器
通过所有这些,您可以执行以下操作
- 定义您的自定义注释,以便您可以从文件中唯一地识别定位器。
- 定义一个自定义注解解析器,它将读取您的自定义注解的属性并构造一个
By 对象
- 定义一个自定义
ElementLocator,以便它使用自定义注释解析器来查找元素。
- 定义一个自定义
ElementLocatorFactory,以便生成自定义ElementLocator 的实例。
我在某个时候将整个概念写成博客。请看一下here
要全面了解 PageFactory 的工作原理,请查看我创建的 this 博客。
有人以我的博客文章为起点,并建立了一个库,它也可以为您完成所有这些工作。你可以从这里消费它https://github.com/shchukax/search-with
为了完整起见,我将我博客中的所有代码 sn-ps 包括在内
json 看起来像这样
[
{
"pageName": "HomePage",
"name": "abTesting",
"locateUsing": "xpath",
"locator": "//a[contains(@href,'abtest')]"
},
{
"pageName": "HomePage",
"name": "checkBox",
"locateUsing": "xpath",
"locator": "//a[contains(@href,'checkboxes')]"
},
{
"pageName": "CheckboxPage",
"name": "checkBox1",
"locateUsing": "xpath",
"locator": "//input[@type='checkbox'][1]"
},
{
"pageName": "CheckboxPage",
"name": "checkBox2",
"locateUsing": "xpath",
"locator": "//input[@type='checkbox'][2]"
}
]
自定义注解如下所示
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.FIELD)
public @interface SearchWith {
String inPage() default "";
String locatorsFile() default "";
String name() default "";
}
自定义ElementLocator 实现
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.pagefactory.AbstractAnnotations;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import java.util.List;
public class FileBasedElementLocator implements ElementLocator {
private final SearchContext searchContext;
private final boolean shouldCache;
private final By by;
private WebElement cachedElement;
private List<WebElement> cachedElementList;
public FileBasedElementLocator(SearchContext searchContext, AbstractAnnotations annotations) {
this.searchContext = searchContext;
this.shouldCache = annotations.isLookupCached();
this.by = annotations.buildBy();
}
@Override
public WebElement findElement() {
if (cachedElement != null && shouldCache) {
return cachedElement;
}
WebElement element = searchContext.findElement(by);
if (shouldCache) {
cachedElement = element;
}
return element;
}
@Override
public List<WebElement> findElements() {
if (cachedElementList != null && shouldCache) {
return cachedElementList;
}
List<WebElement> elements = searchContext.findElements(by);
if (shouldCache) {
cachedElementList = elements;
}
return elements;
}
}
自定义元素定位器工厂如下所示
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import java.lang.reflect.Field;
public class FileBasedElementLocatorFactory implements ElementLocatorFactory {
private final SearchContext searchContext;
public FileBasedElementLocatorFactory(SearchContext searchContext) {
this.searchContext = searchContext;
}
@Override
public ElementLocator createLocator(Field field) {
return new FileBasedElementLocator(searchContext, new CustomAnnotations(field));
}
}
AbstractAnnotations的自定义实现
import com.google.common.base.Preconditions;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.openqa.selenium.By;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.pagefactory.AbstractAnnotations;
import organized.chaos.annotations.SearchWith;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.util.Iterator;
class CustomAnnotations extends AbstractAnnotations {
private final Field field;
CustomAnnotations(Field field) {
this.field = field;
}
@Override
public By buildBy() {
SearchWith search = field.getAnnotation(SearchWith.class);
Preconditions.checkArgument(search != null, "Failed to locate the annotation @SearchWith");
String elementName = search.name();
String pageName = search.inPage();
String locatorsFile = search.locatorsFile();
Preconditions
.checkArgument(isNotNullAndEmpty(elementName), "Element name is not found.");
Preconditions.checkArgument(isNotNullAndEmpty(pageName), "Page name is missing.");
Preconditions.checkArgument(isNotNullAndEmpty(locatorsFile), "Locators File name not provided");
File file = new File(locatorsFile);
Preconditions.checkArgument(file.exists(), "Unable to locate " + locatorsFile);
try {
JsonArray array = new JsonParser().parse(new FileReader(file)).getAsJsonArray();
Iterator&lt;JsonElement&gt; iterator = array.iterator();
JsonObject foundObject = null;
while (iterator.hasNext()) {
JsonObject object = iterator.next().getAsJsonObject();
if (pageName.equalsIgnoreCase(object.get("pageName").getAsString()) &&
elementName.equalsIgnoreCase(object.get("name").getAsString())) {
foundObject = object;
break;
}
}
Preconditions.checkState(foundObject != null, "No entry found for the page [" + pageName + "] in the "
+ "locators file [" + locatorsFile + "]");
String locateUsing = foundObject.get("locateUsing").getAsString();
if (! ("xpath".equalsIgnoreCase(locateUsing))) {
throw new UnsupportedOperationException("Currently " + locateUsing + " is NOT supported. Only xPaths "
+ "are supported");
}
String locator = foundObject.get("locator").getAsString();
Preconditions.checkArgument(isNotNullAndEmpty(locator), "Locator cannot be null (or) empty.");
return new By.ByXPath(locator);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean isLookupCached() {
return (field.getAnnotation(CacheLookup.class) != null);
}
private boolean isNotNullAndEmpty(String arg) {
return ((arg != null) && (! arg.trim().isEmpty()));
}
}
页面对象类如下所示
import org.openqa.selenium.WebElement;
import organized.chaos.annotations.SearchWith;
public class HomePage {
public static final String PAGE = "HomePage";
@SearchWith (inPage = HomePage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "abTesting")
private WebElement abTestingLink = null;
@SearchWith (inPage = HomePage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "checkBox")
private WebElement checkBoxLink = null;
public HomePage() {
}
public CheckBoxPage navigateToCheckBoxPage() {
checkBoxLink.click();
return new CheckBoxPage();
}
}
另一个页面类
import org.openqa.selenium.WebElement;
import organized.chaos.annotations.SearchWith;
public class CheckBoxPage {
private static final String PAGE = "CheckBoxPage";
@SearchWith (inPage = CheckBoxPage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "checkBox1")
private WebElement checkBoxOne;
@SearchWith (inPage = CheckBoxPage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "checkBox2")
private WebElement checkBoxTwo;
public void unCheckCheckBoxTwo() {
if (checkBoxTwo.isSelected()) {
checkBoxTwo.click();
}
}
public boolean isCheckBoxTwoUnchecked() {
return (! checkBoxTwo.isSelected());
}
}
这是一个使用它的测试类
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import organized.chaos.pages.CheckBoxPage;
import organized.chaos.pages.HomePage;
import organized.chaos.support.FileBasedElementLocatorFactory;
public class AlteredPageFactoryDemo {
private RemoteWebDriver driver;
@BeforeClass
public void setup() {
driver = new ChromeDriver();
}
@AfterClass
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
@Test
public void testMethod() {
driver.get("https://the-internet.herokuapp.com/");
HomePage homePage = new HomePage();
ElementLocatorFactory factory = new FileBasedElementLocatorFactory(driver);
PageFactory.initElements(factory, homePage);
CheckBoxPage checkboxPage = homePage.navigateToCheckBoxPage();
PageFactory.initElements(factory, checkboxPage);
checkboxPage.unCheckCheckBoxTwo();
Assert.assertTrue(checkboxPage.isCheckBoxTwoUnchecked());
}
}