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

Cucumber+Selenium Java:测试前单次登录并保持WebDriver会话需求

哥们,这个问题我太有发言权了!之前帮团队搞定过几乎一模一样的情况——登录慢到离谱,所有测试又都得从登录后的页面开始,总不能每个场景都等10分钟吧?核心思路就是复用同一个WebDriver会话,只在所有测试启动前登录一次,之后所有场景共享这个会话。下面给你一步步拆解实现方案:

核心思路:复用WebDriver实例 + Cucumber全局钩子

1. 用单例模式管理WebDriver(串行测试首选)

先写一个DriverManager类,确保整个测试过程中只有一个WebDriver实例,避免重复初始化:

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

public class DriverManager {
    // 用volatile保证多线程下的实例可见性
    private static volatile WebDriver driver;

    // 私有构造,防止外部随意实例化
    private DriverManager() {}

    public static WebDriver getDriver() {
        // 双重检查锁实现线程安全的单例
        if (driver == null) {
            synchronized (DriverManager.class) {
                if (driver == null) {
                    System.setProperty("webdriver.chrome.driver", "你的chromedriver本地路径");
                    driver = new ChromeDriver();
                    driver.manage().window().maximize();
                }
            }
        }
        return driver;
    }

    // 测试结束后统一关闭Driver
    public static void quitDriver() {
        if (driver != null) {
            driver.quit();
            driver = null; // 重置实例,方便下次测试执行
        }
    }
}

2. 用Cucumber全局钩子执行一次登录

创建一个全局钩子类,用@BeforeAll在所有场景启动前执行一次登录操作,@AfterAll在所有测试结束后关闭Driver:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import io.cucumber.java.AfterAll;
import io.cucumber.java.BeforeAll;
import java.time.Duration;

public class GlobalTestHooks {
    private static WebDriver driver;

    @BeforeAll
    public static void setupAndLogin() {
        driver = DriverManager.getDriver();
        // 替换成你的实际登录流程
        driver.get("你的登录页面URL");
        
        // 输入账号密码(换成你页面的元素定位器)
        driver.findElement(By.id("username")).sendKeys("你的测试账号");
        driver.findElement(By.id("password")).sendKeys("你的测试密码");
        driver.findElement(By.id("login-btn")).click();

        // 因为登录要10分钟,设置足够长的显式等待,直到进入目标页面
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofMinutes(10));
        wait.until(ExpectedConditions.titleContains("目标页面的标题关键字"));
    }

    @AfterAll
    public static void teardown() {
        DriverManager.quitDriver();
    }
}

3. 步骤定义中复用已登录的Driver

在你的Step Definition类里,直接通过DriverManager.getDriver()拿到已登录的Driver实例,不用每次重新初始化:

import org.openqa.selenium.WebDriver;
import io.cucumber.java.en.Given;

public class YourStepDefinitions {
    private final WebDriver driver;

    // 构造方法中获取Driver实例
    public YourStepDefinitions() {
        this.driver = DriverManager.getDriver();
    }

    @Given("我已在目标页面")
    public void verifyOnTargetPage() {
        // 直接使用driver操作,此时已经是登录状态
        assert driver.getTitle().contains("目标页面标题");
    }

    // 其他步骤定义同理,直接复用driver即可
}

4. 场景间状态重置(可选但重要)

因为所有场景共用同一个Driver,前一个场景的操作可能会修改页面状态,影响下一个场景的初始条件。可以加一个@Before钩子,在每个场景前重置到目标页面:

import io.cucumber.java.Before;

public class ScenarioHooks {
    private final WebDriver driver;

    public ScenarioHooks() {
        this.driver = DriverManager.getDriver();
    }

    @Before
    public void resetToTargetPage() {
        // 每次场景开始前,跳回目标页面
        driver.get("你的目标页面URL");
        // 如果需要清除本地存储/会话存储,可执行JS:
        // driver.executeScript("window.localStorage.clear(); window.sessionStorage.clear();");
    }
}

进阶:并行测试的处理(如果需要)

如果你的测试需要并行执行,单例模式会导致多个场景共享Driver冲突。这时候可以改用ThreadLocal为每个线程维护独立的Driver实例:

修改DriverManager为ThreadLocal版本:

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

public class DriverManager {
    private static final ThreadLocal<WebDriver> driverThreadLocal = new ThreadLocal<>();

    private DriverManager() {}

    public static WebDriver getDriver() {
        if (driverThreadLocal.get() == null) {
            System.setProperty("webdriver.chrome.driver", "你的chromedriver路径");
            WebDriver driver = new ChromeDriver();
            driver.manage().window().maximize();
            driverThreadLocal.set(driver);
        }
        return driverThreadLocal.get();
    }

    public static void quitDriver() {
        WebDriver driver = driverThreadLocal.get();
        if (driver != null) {
            driver.quit();
            driverThreadLocal.remove();
        }
    }
}

并行场景下减少登录等待的小技巧

如果并行时不想每个线程都等10分钟登录,可以尝试保存登录Cookie复用:

  1. 先单独运行一次登录流程,把Cookie保存到本地文件;
  2. 每个线程启动时,直接加载Cookie到Driver,跳过登录步骤。

示例代码(在全局钩子中保存Cookie):

import org.openqa.selenium.Cookie;
import java.io.*;
import java.util.Set;

@BeforeAll
public static void setupAndSaveCookie() {
    WebDriver driver = DriverManager.getDriver();
    // 执行登录操作...
    // 保存Cookie到文件
    try (FileOutputStream fos = new FileOutputStream("login-cookies.data");
         ObjectOutputStream oos = new ObjectOutputStream(fos)) {
        oos.writeObject(driver.manage().getCookies());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

然后在每个场景前加载Cookie:

@Before
public void loadLoginCookie() {
    WebDriver driver = DriverManager.getDriver();
    // 先打开应用根域名,才能加载Cookie
    driver.get("你的应用根域名");
    // 加载Cookie
    try (FileInputStream fis = new FileInputStream("login-cookies.data");
         ObjectInputStream ois = new ObjectInputStream(fis)) {
        Set<Cookie> cookies = (Set<Cookie>) ois.readObject();
        for (Cookie cookie : cookies) {
            driver.manage().addCookie(cookie);
        }
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
    // 刷新后即可进入登录状态
    driver.navigate().refresh();
}

注意:这个方案要验证Cookie的有效期,以及应用是否有会话验证机制(比如CSRF Token)导致Cookie失效。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:39:30