You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Playwright(TypeScript)中实现Selenium的PageFactory与@FindBy注解?

实现类似Selenium PageFactory与@FindBy注解的方案

1. 自定义@FindBy注解

先定义运行时保留的注解,用于标记页面对象中的元素字段,支持常见定位方式:

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 FindBy {
    String id() default "";
    String name() default "";
    String xpath() default "";
    String cssSelector() default "";
    // 可按需扩展className、tagName等定位方式
}

2. 实现页面对象工厂类

通过反射读取注解信息,自动查找并初始化页面对象中的元素,加入显式等待避免元素加载问题:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.lang.reflect.Field;
import java.time.Duration;

public class CustomPageFactory {
    public static <T> T initElements(Class<T> pageClass, WebDriver driver) {
        try {
            T pageInstance = pageClass.getDeclaredConstructor().newInstance();
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
            Field[] fields = pageClass.getDeclaredFields();

            for (Field field : fields) {
                if (field.isAnnotationPresent(FindBy.class)) {
                    FindBy annotation = field.getAnnotation(FindBy.class);
                    WebElement element = locateElement(driver, wait, annotation);

                    if (element != null) {
                        field.setAccessible(true);
                        field.set(pageInstance, element);
                    }
                }
            }
            return pageInstance;
        } catch (Exception e) {
            throw new RuntimeException("页面对象初始化失败", e);
        }
    }

    private static WebElement locateElement(WebDriver driver, WebDriverWait wait, FindBy annotation) {
        if (!annotation.id().isEmpty()) {
            return wait.until(ExpectedConditions.presenceOfElementLocated(By.id(annotation.id())));
        } else if (!annotation.name().isEmpty()) {
            return wait.until(ExpectedConditions.presenceOfElementLocated(By.name(annotation.name())));
        } else if (!annotation.xpath().isEmpty()) {
            return wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(annotation.xpath())));
        } else if (!annotation.cssSelector().isEmpty()) {
            return wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(annotation.cssSelector())));
        }
        return null;
    }
}

3. 页面对象使用示例

创建页面对象类,用@FindBy标记元素字段:

import org.openqa.selenium.WebElement;

public class LoginPage {
    @FindBy(id = "user-name")
    private WebElement usernameInput;

    @FindBy(cssSelector = "#password")
    private WebElement passwordInput;

    @FindBy(xpath = "//input[@type='submit']")
    private WebElement loginButton;

    public void enterCredentials(String username, String password) {
        usernameInput.sendKeys(username);
        passwordInput.sendKeys(password);
    }

    public void clickLogin() {
        loginButton.click();
    }
}

初始化并使用页面对象:

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class TestRunner {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        driver.get("https://example-login-page.com");

        LoginPage loginPage = CustomPageFactory.initElements(LoginPage.class, driver);
        loginPage.enterCredentials("test_user", "test_pass");
        loginPage.clickLogin();

        driver.quit();
    }
}

4. 扩展与优化建议

  • 补充更多定位方式:在@FindBy注解中添加className、tagName、linkText等属性,并在locateElement方法中补充对应逻辑
  • 适配动态元素:加入重试机制,或允许在注解中配置自定义等待时长
  • 支持嵌套页面对象:在工厂类中添加递归逻辑,初始化页面对象中的嵌套页面对象字段
  • 添加元素缓存:避免重复查找相同元素,提升执行效率

内容的提问来源于stack exchange,提问作者חיים מור

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 16:27:39