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

Java11嵌套类结合流畅设计重构Selenium自动化测试项目求助

Fluent Design Refactor for Java Selenium Page Objects (With Nested Classes)

Hey there! Let's work through refactoring your Selenium/TestNG project to use fluent design while keeping those nested page object classes—since you're comfortable with C# but new to Java's nested class behavior, I'll break this down step by step to get that smooth method chain working.

Key Issues in Your Current Code

Before diving into fixes, let's call out what's blocking the fluent flow:

  • The AddEditUserInformation nested class is static, so it has no link to the parent UserAdminPage instance—you can't seamlessly return to parent methods after using nested class logic.
  • Some methods return void or incorrect types (like FirstName_txtbx returning UserAdminPage instead of the nested class), which breaks the chain.
  • Your test code's syntax for calling nested class methods isn't valid in Java—you need an instance of the nested class to call its methods, not just the class name.

Refactored Page Object Code

Here's the adjusted UserAdminPage that preserves nested classes and enables fluent calls:

import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class UserAdminPage {
    private final RemoteWebDriver driver;

    public UserAdminPage(RemoteWebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    // Entry point for fluent chain (matches your existing using pattern)
    public static UserAdminPage using(RemoteWebDriver driver) {
        return new UserAdminPage(driver);
    }

    // Non-static nested class (inner class) - automatically holds a reference to the parent UserAdminPage instance
    public class AddEditUserInformation {
        @FindBy(name = "firstName")
        private WebElement firstNameTxtbx;

        @FindBy(name = "lastName")
        private WebElement lastNameTxtbx;

        // Return the nested class instance to keep the chain going for related actions
        public AddEditUserInformation setFirstName(String firstName) {
            firstNameTxtbx.sendKeys(firstName);
            return this;
        }

        // Return the parent UserAdminPage instance to jump back to parent-level actions
        public UserAdminPage setLastName(String lastName) {
            lastNameTxtbx.sendKeys(lastName);
            return UserAdminPage.this; // Reference the parent class instance
        }
    }

    // Method to get the nested class instance - starts the nested action chain
    public AddEditUserInformation addEditUser() {
        return new AddEditUserInformation();
    }

    @FindBy(id = "AccountsList")
    private WebElement accountNameDropdown;

    // Return parent instance to continue the chain
    public UserAdminPage selectAccountName() {
        accountNameDropdown.click();
        return this;
    }

    @FindBy(id = "Save_button")
    private WebElement saveBtn;

    // Optional: return parent instance if you want to chain more actions after save
    public UserAdminPage save() {
        saveBtn.click();
        return this;
    }
}

Fluent Test Case Example

Now your test code becomes a readable, uninterrupted chain—perfect for non-developers to follow:

UserAdminPage.using(driver)
    .selectAccountName()
    .addEditUser()
        .setFirstName("John")
        .setLastName("Doe")
    .save();

Critical Concepts Explained

  1. Non-Static Nested Classes (Inner Classes)
    Unlike static nested classes, Java inner classes automatically hold a reference to their parent class instance. This lets us easily jump back to parent methods (like save()) after completing nested actions using UserAdminPage.this.

  2. Consistent Return Types
    Every method that needs to keep the fluent chain alive returns either:

    • this (the current class instance) to continue actions in the same scope
    • The target class instance (parent or nested) to switch scopes smoothly
  3. Readable Naming
    I renamed methods to use action-based names (e.g., selectAccountName() instead of AccountName_dropdwn(), setFirstName() instead of FirstName_txtbx())—this makes the test case read like plain English, which is key for non-developer users.

If You Must Keep Static Nested Classes

If you need AddEditUserInformation to stay static (for specific project constraints), you can manually pass the parent instance to the nested class constructor:

// Static nested class with parent reference
public static class AddEditUserInformation {
    private final UserAdminPage parentPage;

    public AddEditUserInformation(UserAdminPage parentPage) {
        this.parentPage = parentPage;
    }

    public AddEditUserInformation setFirstName(String firstName) {
        PageFactory.initElements(parentPage.driver, this);
        firstNameTxtbx.sendKeys(firstName);
        return this;
    }

    public UserAdminPage setLastName(String lastName) {
        lastNameTxtbx.sendKeys(lastName);
        return parentPage;
    }
}

// Parent class method to get nested instance
public AddEditUserInformation addEditUser() {
    return new AddEditUserInformation(this);
}

This achieves the same fluent chain while keeping the nested class static.

内容的提问来源于stack exchange,提问作者Handsome Coder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:44:19