Selenium自动化测试八股文

1. 什么是自动化测试 / 测试自动化?

自动化测试,就是测试人员提前写好测试脚本,让代码代替人工,自动跑完所有测试步骤、核对结果对不对,最后直接出测试报告,不用人反复手动点来点去做重复测试。

2. 自动化测试的优势是什么?

  • 提效省力:替代人工重复操作,支持无人值守并行执行,完美适配回归测试。
  • 精准稳定:消除人为测试误差,执行标准统一,结果可复现、校验更精准。
  • 场景全覆盖:可完成人工难以落地的性能、兼容、异常极限等场景测试。
  • 赋能研发:对接 CI/CD 流程,实现测试左移,助力产品高效稳定迭代。
  • 降本控险:长期边际成本极低,提前拦截缺陷,测试全程可追溯、满足合规要求。

3. Selenium 支持哪些测试类型?

功能测试、回归测试、健全性测试、冒烟测试、响应式测试、跨浏览器测试、UI 测试、集成测试。

4. 在 Selenium 中定位 Web 元素有哪些方法?

共有 8 种核心定位方法,按使用优先级排序如下:

  1. id:元素唯一标识,定位精准、速度最快
  2. name:元素 name 属性,常用于表单元素
  3. class name:元素样式类名
  4. tag name:元素标签名(如 input、button)
  5. link text:超链接完整文本,精准匹配
  6. partial link text:超链接部分文本,模糊匹配
  7. css selector:CSS 选择器,灵活高效,兼容性强
  8. xpath:路径表达式,万能定位,支持复杂场景

代码示例

元素定位是自动化测试的基础,以下是常用定位方式的代码示例:

Java 代码示例
// Java版本 元素定位示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class ElementLocatorDemo {
    public static void main(String[] args) {
        // 初始化驱动
        WebDriver driver = new ChromeDriver();
        driver.get("https://www.example.com");

        // 1. 通过id定位
        WebElement elementById = driver.findElement(By.id("username"));
        // 2. 通过name定位
        WebElement elementByName = driver.findElement(By.name("password"));
        // 3. 通过xpath定位
        WebElement elementByXpath = driver.findElement(By.xpath("//input[@placeholder='请输入手机号']"));
        // 4. 通过css selector定位
        WebElement elementByCss = driver.findElement(By.cssSelector("button.login-btn"));
        // 5. 通过link text定位
        WebElement elementByLink = driver.findElement(By.linkText("联系我们"));

        // 关闭驱动
        driver.quit();
    }
}
Python 代码示例
# Python版本 元素定位示例
from selenium import webdriver
from selenium.webdriver.common.by import By

# 初始化驱动
driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 1. 通过id定位
element_by_id = driver.find_element(By.ID, "username")
# 2. 通过name定位
element_by_name = driver.find_element(By.NAME, "password")
# 3. 通过xpath定位
element_by_xpath = driver.find_element(By.XPATH, "//input[@placeholder='请输入手机号']")
# 4. 通过css selector定位
element_by_css = driver.find_element(By.CSS_SELECTOR, "button.login-btn")
# 5. 通过link text定位
element_by_link = driver.find_element(By.LINK_TEXT, "联系我们")

# 关闭驱动
driver.quit()

5. 可以与 Selenium 集成以实现持续测试的自动化工具有哪些?

  • 测试用例管理 & 报告生成工具:JUnit、TestNG
  • 持续集成 & 自动触发工具:Jenkins、Maven、Docker

一句话说明:你改了软件代码,Jenkins 能自动让 Selenium 跑一遍所有测试,然后直接告诉你哪些功能坏了。

6. 断言和验证命令的区别?

  • 断言(assert):检查点不通过时,测试立刻终止,后续步骤不再执行。比如登录失败了,就直接停了,不会再去测登录后的功能,适合核心流程校验。
  • 验证(verify):检查点不通过时,测试仍会继续执行完所有步骤,最终统一汇总所有失败项,不会中途终止,适合非核心的多字段批量校验。

7. 绝对 XPath 和属性 XPath 是什么?

  • 绝对 XPath:从网页最开头的根标签(html)开始,一层一层写到目标元素的完整路径,就像写完整家庭地址 “中国 / XX 省 / XX 市 / XX 区 / XX 小区 / XX 栋 / XX 室”。
    • 缺点:网页只要改了一层结构,这个路径就会失效,稳定性极差,工作中基本不使用。
    • 示例:/html/body/div[2]/div/form/div[1]/input
  • 属性 XPath:通过元素的自带属性(比如 id、name、placeholder、text 文本等)定位元素,就像找 “身份证号是 XXX 的人”,不管它在网页的哪个位置,只要属性匹配就能找到,网页结构改动也不影响,稳定性强,是工作中的主流用法。
    • 示例://input[@id='username' and @placeholder='请输入用户名']

8. XPath 里 “/” 和 “//” 的区别?

  • /:表示绝对路径的层级分隔,必须从当前节点的直接子节点中匹配,一层一层往下找,不能跳级,只能找 “亲儿子” 元素。
  • //:表示相对路径,可在整个 HTML 文档中任意位置匹配符合条件的元素,无视节点层级,可跳级查找,灵活性极强,是日常使用的主流写法。

9. typeKeys() 和 type() 命令的区别?

两者均为 Selenium 早期版本中往输入框填写内容的命令,核心区别在于是否触发键盘相关事件:

  • typeKeys():填写内容时,会触发网页的键盘事件(keydown、keyup、keypress),和真人手动在键盘上打字的效果完全一致。比如输密码时,网页会实时提示密码强弱,这个实时校验功能会被正常触发。
  • type():仅把内容直接填入输入框的 value 属性中,不会触发任何键盘事件,网页上的实时校验、输入联想等功能不会生效。

10. type 和 typeAndWait 命令的区别?

  • type:仅往输入框中填写内容,填写完成后立刻执行下一个步骤,不会等待网页刷新或跳转。适合填写内容后无页面刷新的场景。
  • typeAndWait:填写内容后,会等待网页完成刷新 / 跳转,再执行下一个步骤。适合填写内容后会触发页面重新加载的场景(比如搜索框输入关键词后自动跳转搜索结果页)。

11. findElement() 和 findElements() 的区别?

两者均为 Selenium 中查找网页元素的核心方法,核心区别如下:

  • findElement():匹配页面中第一个符合条件的元素,仅返回单个元素对象。如果页面中没有符合条件的元素,会直接抛出 NoSuchElementException 异常,终止测试执行。
  • findElements():匹配页面中所有符合条件的元素,返回一个元素列表。如果页面中没有符合条件的元素,会返回一个空列表,不会抛出异常,测试会继续执行。

12. 刷新网页有哪些方法?

Selenium 中常用的网页刷新方法有 5 种:

Java 代码示例
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

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

        // 方法1:navigate().refresh() 【最常用,等效手动按F5】
        driver.navigate().refresh();

        // 方法2:重新获取当前URL,用get()方法重新打开
        String currentUrl = driver.getCurrentUrl();
        driver.get(currentUrl);

        // 方法3:重新获取当前URL,用navigate().to()方法跳转
        driver.navigate().to(driver.getCurrentUrl());

        // 方法4:模拟键盘按下F5键刷新
        WebElement bodyElement = driver.findElement(By.tagName("body"));
        bodyElement.sendKeys(Keys.F5);

        // 方法5:通过F5键的Unicode编码模拟按键刷新
        bodyElement.sendKeys("\uE035");

        driver.quit();
    }
}
Python 代码示例
# Python版本 网页刷新示例
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 方法1:refresh() 【最常用,等效手动按F5】
driver.refresh()

# 方法2:重新获取当前URL,用get()方法重新打开
current_url = driver.current_url
driver.get(current_url)

# 方法3:模拟键盘按下F5键刷新
body_element = driver.find_element(By.TAG_NAME, "body")
body_element.send_keys(Keys.F5)

driver.quit()

13. 怎么实现浏览器的前进和后退?

Selenium 中通过 navigate 接口直接实现浏览器的前进后退,等效手动点击浏览器左上角的前进 / 后退按钮。

使用说明

  • navigate().back() - 后退到上一个页面
  • navigate().forward() - 前进到下一个页面(需要先执行过后退)
Java 代码示例
// Java版本 浏览器前进后退示例
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class BrowserNavigateDemo {
    public static void main(String[] args) throws InterruptedException {
        WebDriver driver = new ChromeDriver();

        // 打开第一个页面
        driver.get("https://www.example.com/page1");
        Thread.sleep(1000); // 等待页面加载,仅演示用,实际推荐用显式等待

        // 打开第二个页面
        driver.get("https://www.example.com/page2");
        Thread.sleep(1000);

        // 后退到上一个页面(page1)
        driver.navigate().back();
        System.out.println("当前页面:" + driver.getCurrentUrl());
        Thread.sleep(1000);

        // 前进到下一个页面(page2)
        driver.navigate().forward();
        System.out.println("当前页面:" + driver.getCurrentUrl());

        driver.quit();
    }
}
Python 代码示例
# Python版本 浏览器前进后退示例
from selenium import webdriver
import time

driver = webdriver.Chrome()

# 打开第一个页面
driver.get("https://www.example.com/page1")
time.sleep(1)

# 打开第二个页面
driver.get("https://www.example.com/page2")
time.sleep(1)

# 后退到上一个页面
driver.back()
print("当前页面:", driver.current_url)
time.sleep(1)

# 前进到下一个页面
driver.forward()
print("当前页面:", driver.current_url)

driver.quit()

14. 怎么获取网页元素的文本?

核心通过 getText() 方法获取元素的可见文本,同时可通过 getAttribute() 方法获取元素的其他内容属性。

Java 代码示例
// Java版本 获取元素文本示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

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

        // 先定位目标元素
        WebElement targetElement = driver.findElement(By.id("submitBtn"));
        WebElement inputElement = driver.findElement(By.id("username"));

        // 1. 获取元素的可见文本(最常用,比如按钮、标签上的文字)
        String elementText = targetElement.getText();
        System.out.println("元素可见文本:" + elementText);

        // 2. 获取输入框中用户输入/预设的内容
        String inputValue = inputElement.getAttribute("value");
        System.out.println("输入框内容:" + inputValue);

        // 3. 补充:获取元素的完整HTML内容
        String elementHtml = targetElement.getAttribute("outerHTML");
        System.out.println("元素完整HTML:" + elementHtml);

        driver.quit();
    }
}
Python 代码示例
# Python版本 获取元素文本示例
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 先定位目标元素
target_element = driver.find_element(By.ID, "submitBtn")
input_element = driver.find_element(By.ID, "username")

# 1. 获取元素的可见文本(最常用,比如按钮、标签上的文字)
element_text = target_element.text
print("元素可见文本:" + element_text)

# 2. 获取输入框中用户输入/预设的内容
input_value = input_element.get_attribute("value")
print("输入框内容:" + input_value)

# 3. 补充:获取元素的完整HTML内容
element_html = target_element.get_attribute("outerHTML")
print("元素完整HTML:" + element_html)

driver.quit()

15. 有哪些导航命令?

Selenium 中核心的浏览器导航命令共 4 个,均通过 driver.navigate() 调用:

  1. navigate().to(String url):跳转到指定的网址,和 driver.get(url) 效果完全一致
  2. navigate().back():浏览器后退,回到历史记录中的上一个页面
  3. navigate().forward():浏览器前进,进入历史记录中的下一个页面(需先执行过后退操作)
  4. navigate().refresh():刷新当前页面,等效手动按下 F5 键

16. 怎么处理网页里的框架(iframe)?

iframe 是网页中嵌套的独立子页面,Selenium 默认只能操作最外层的主页面,要操作 iframe 内的元素,必须先切换到对应 iframe 中,操作完成后切回主页面。

Java 代码示例
// Java版本 iframe处理示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

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

        // 方法1:通过iframe的索引切换(从0开始,页面第一个iframe索引为0)
        driver.switchTo().frame(0);

        // 方法2:通过iframe的id属性切换【最常用,稳定性最强】
        driver.switchTo().frame("login-iframe");

        // 方法3:通过iframe的name属性切换
        driver.switchTo().frame("iframe-name");

        // 方法4:先定位iframe元素,再切换(适配无id/name的场景)
        WebElement iframeElement = driver.findElement(By.xpath("//iframe[@title='登录弹窗']"));
        driver.switchTo().frame(iframeElement);

        // ========== 切换完成后,即可操作iframe内的元素 ==========
        driver.findElement(By.id("username")).sendKeys("test123");

        // ========== 操作完成后,切回主页面/父级iframe ==========
        // 1. 切回最外层的主页面(最常用)
        driver.switchTo().defaultContent();
        // 2. 切回上一级父iframe(适配多层iframe嵌套场景)
        driver.switchTo().parentFrame();

        driver.quit();
    }
}
Python 代码示例
# Python版本 iframe处理示例
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 方法1:通过iframe的索引切换(从0开始,页面第一个iframe索引为0)
driver.switch_to.frame(0)

# 方法2:通过iframe的id属性切换【最常用,稳定性最强】
driver.switch_to.frame("login-iframe")

# 方法3:通过iframe的name属性切换
driver.switch_to.frame("iframe-name")

# 方法4:先定位iframe元素,再切换(适配无id/name的场景)
iframe_element = driver.find_element(By.XPATH, "//iframe[@title='登录弹窗']")
driver.switch_to.frame(iframe_element)

# ========== 切换完成后,即可操作iframe内的元素 ==========
driver.find_element(By.ID, "username").send_keys("test123")

# ========== 操作完成后,切回主页面/父级iframe ==========
# 1. 切回最外层的主页面(最常用)
driver.switch_to.default_content()
# 2. 切回上一级父iframe(适配多层iframe嵌套场景)
driver.switch_to.parent_frame()

driver.quit()

17. 什么是 POM (页面对象模型)?【面试高频】

一、先明确:POM = PO,是同一个东西

  • PO(Page Object):页面对象,指把单个页面封装成一个独立对象(类),里面只放这个页面的元素定位、操作方法
  • POM(Page Object Model):页面对象模型,是基于 PO 的一套完整设计模式 / 规范—— 核心就是「页面 = PO 类、测试脚本 = 业务逻辑、两者彻底分离」

重要:日常说的 PO 模式、POM 模式、页面对象模式,完全是一回事,只是叫法不同,核心思想完全一致。


二、POM(PO)的核心设计思想(一句话讲透)

把每个页面封装成一个独立的 PO 类:

  • 每个页面(登录页、首页、搜索页)对应一个专属 PO 类(如 LoginPage、HomePage)
  • PO 类里只做两件事:封装页面所有元素定位、封装页面所有操作方法(输入、点击、断言等)
  • 测试脚本里不写任何元素定位、不写任何页面细节,只调用 PO 类的方法,写业务流程(如:打开登录页→输入账号→点击登录→断言登录成功)

一句话总结:页面变改 PO,流程变改脚本,互不干扰


三、POM(PO)的三层标准结构(最规范写法,面试 / 工作通用)

1. 基础层(BasePage)—— 所有 PO 类的父类(必写,解决重复代码)
  • 封装所有页面通用方法:元素等待、截图、切换 iframe、窗口、获取文本、点击、输入等
  • 所有 PO 类都继承 BasePage,不用重复写通用逻辑
2. 页面对象层(PO 层)—— 每个页面对应一个类(核心)
  • 每个页面一个 PO 类(LoginPage、HomePage)
  • 类里只放:元素定位(By 对象)、页面操作方法(输入、点击、选择等)
  • 方法只做页面操作,不写断言、不写业务逻辑
3. 测试用例层(Test 层)—— 写测试流程、断言、数据驱动
  • 只调用 PO 类的方法,写完整业务流程(如登录→搜索→下单→断言)
  • 负责:用例组织、数据驱动、断言、报告生成、异常处理
  • 完全不接触元素定位、页面细节

四、POM(PO)的核心优点(对比不使用 PO 的原始写法)

优点说明原始写法对比
易维护(最大优势)页面元素改了(如 id 变了、按钮位置变了),只改对应 PO 类里的 1 处定位,所有调用这个方法的测试脚本都不用改每个脚本都写定位,改一次要改 N 处,极易漏改、出错
高复用页面操作(如登录、搜索)封装成方法,所有测试用例都能直接调用,不用重复写代码每个用例都复制粘贴登录代码,冗余、难维护
可读性强、易协作测试脚本全是业务语义:loginPage.inputUser("test").inputPwd("123").clickLogin()不懂代码的人也能看懂流程,方便测试、开发、产品协作
解耦、降低耦合度页面 UI(PO)和测试逻辑(Test)彻底分离:页面改→改 PO,流程改→改 Test,互不影响定位和逻辑混在一起,牵一发而动全身
可扩展性好新增页面→新增 PO 类;新增用例→新增 Test 方法,不影响原有代码-

五、POM(PO)的完整代码示例(Java,最常用)

1. 基础层:BasePage(父类,封装通用方法)
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;

// 所有PO类的父类,封装通用操作
public class BasePage {
    protected WebDriver driver;
    protected WebDriverWait wait;

    // 构造方法,初始化驱动和等待
    public BasePage(WebDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    }

    // 通用:点击元素(带等待)
    protected void click(By locator) {
        wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
    }

    // 通用:输入文本
    protected void sendKeys(By locator, String text) {
        wait.until(ExpectedConditions.visibilityOfElementLocated(locator)).clear();
        wait.until(ExpectedConditions.visibilityOfElementLocated(locator)).sendKeys(text);
    }

    // 通用:获取元素文本
    protected String getText(By locator) {
        return wait.until(ExpectedConditions.visibilityOfElementLocated(locator)).getText();
    }

    // 通用:等待元素可见
    protected void waitForVisible(By locator) {
        wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
    }
}
2. PO 层:LoginPage(登录页对象,继承 BasePage)
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

// 登录页PO类:只封装登录页的元素和操作
public class LoginPage extends BasePage {
    // 1. 元素定位(只在这里定义,统一管理)
    private By usernameInput = By.id("username");
    private By passwordInput = By.id("password");
    private By loginBtn = By.id("login-btn");
    private By errorMsg = By.className("error-tip");

    // 构造方法
    public LoginPage(WebDriver driver) {
        super(driver);
    }

    // 2. 页面操作方法(只做操作,不写断言)
    // 输入用户名
    public LoginPage inputUsername(String username) {
        sendKeys(usernameInput, username);
        return this; // 链式调用
    }

    // 输入密码
    public LoginPage inputPassword(String password) {
        sendKeys(passwordInput, password);
        return this;
    }

    // 点击登录按钮
    public void clickLogin() {
        click(loginBtn);
    }

    // 获取错误提示文本(供测试层断言用)
    public String getErrorMsg() {
        return getText(errorMsg);
    }
}
3. 测试层:LoginTest(测试用例,调用 PO,写流程 + 断言)
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

// 测试用例:只写业务流程、断言,不写元素定位
public class LoginTest {
    private WebDriver driver;
    private LoginPage loginPage;

    @BeforeMethod
    public void setUp() {
        driver = new ChromeDriver();
        driver.get("https://xxx.com/login");
        loginPage = new LoginPage(driver); // 初始化登录页PO
    }

    // 测试用例1:登录成功
    @Test
    public void testLoginSuccess() {
        // 调用PO方法,写业务流程
        loginPage.inputUsername("test123")
                 .inputPassword("123456")
                 .clickLogin();
        // 断言:登录后跳转到首页
        Assert.assertEquals(driver.getTitle(), "首页");
    }

    // 测试用例2:登录失败(密码错误)
    @Test
    public void testLoginFail() {
        loginPage.inputUsername("test123")
                 .inputPassword("wrong")
                 .clickLogin();
        // 断言:错误提示正确
        Assert.assertEquals(loginPage.getErrorMsg(), "用户名或密码错误");
    }

    @AfterMethod
    public void tearDown() {
        driver.quit();
    }
}

六、POM(PO)的 Python 完整示例

1. 基础层:BasePage
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def click(self, locator):
        """点击元素"""
        self.wait.until(EC.element_to_be_clickable(locator)).click()

    def send_keys(self, locator, text):
        """输入文本"""
        element = self.wait.until(EC.visibility_of_element_located(locator))
        element.clear()
        element.send_keys(text)

    def get_text(self, locator):
        """获取文本"""
        return self.wait.until(EC.visibility_of_element_located(locator)).text
2. PO 层:LoginPage
from selenium.webdriver.common.by import By
from base_page import BasePage

class LoginPage(BasePage):
    # 元素定位
    USERNAME_INPUT = (By.ID, "username")
    PASSWORD_INPUT = (By.ID, "password")
    LOGIN_BTN = (By.ID, "login-btn")
    ERROR_MSG = (By.CLASS_NAME, "error-tip")

    def input_username(self, username):
        self.send_keys(self.USERNAME_INPUT, username)
        return self

    def input_password(self, password):
        self.send_keys(self.PASSWORD_INPUT, password)
        return self

    def click_login(self):
        self.click(self.LOGIN_BTN)

    def get_error_msg(self):
        return self.get_text(self.ERROR_MSG)
3. 测试层:test_login.py
import pytest
from selenium import webdriver
from login_page import LoginPage

@pytest.fixture
def driver():
    driver = webdriver.Chrome()
    driver.get("https://xxx.com/login")
    yield driver
    driver.quit()

def test_login_success(driver):
    login_page = LoginPage(driver)
    login_page.input_username("test123").input_password("123456").click_login()
    assert driver.title == "首页"

def test_login_fail(driver):
    login_page = LoginPage(driver)
    login_page.input_username("test123").input_password("wrong").click_login()
    assert login_page.get_error_msg() == "用户名或密码错误"

七、POM(PO)的常见误区(避坑)

错误做法正确做法
❌ PO 里写断言、写业务逻辑✅ PO 只封装页面操作,断言、流程必须放 Test 层
❌ 一个 PO 类写多个页面✅ 一个页面 = 一个 PO 类,保持单一职责
❌ 定位写在测试脚本里✅ 定位必须全部封装在 PO 类,脚本只调用方法
❌ 不写 BasePage,每个 PO 重复写等待、点击✅ BasePage 是规范,减少冗余
❌ PO 方法里直接 driver.quit()✅ 驱动管理放在 Test 层的 setUp/tearDown

18. 怎么用 Selenium 截图?

Selenium 中通过 TakesScreenshot 接口实现截图功能,核心有 3 种常用用法:

  1. 截取整个浏览器窗口
  2. 截取指定的单个元素
  3. 截图为 Base64 格式(用于测试报告)

核心用法 1:截取整个浏览器窗口【最常用,测试失败留痕】

Java 代码示例
// Java版本 全窗口截图
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import java.io.File;
import java.io.IOException;

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

        try {
            // 1. 将WebDriver对象强转为截图接口类型
            TakesScreenshot screenshot = (TakesScreenshot) driver;
            // 2. 调用截图方法,生成临时图片文件
            File srcFile = screenshot.getScreenshotAs(OutputType.FILE);
            // 3. 定义截图保存路径和文件名
            File destFile = new File("./screenshot/登录页面截图.png");
            // 4. 将临时文件复制到目标路径
            FileUtils.copyFile(srcFile, destFile);
            System.out.println("截图已保存至:" + destFile.getAbsolutePath());
        } catch (IOException e) {
            System.out.println("截图失败:" + e.getMessage());
        }

        driver.quit();
    }
}
Python 代码示例
# Python版本 全窗口截图
from selenium import webdriver
import os

# 确保截图目录存在
if not os.path.exists("./screenshot"):
    os.makedirs("./screenshot")

driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 直接调用get_screenshot_as_file方法,传入保存路径即可
# 括号内为截图保存路径,支持相对路径和绝对路径
driver.get_screenshot_as_file("./screenshot/登录页面截图.png")
print("截图已保存完成")

driver.quit()

核心用法 2:截取指定的单个元素【精准截图,只保留目标区域】

Java 代码示例
// Java版本 单个元素截图
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

import java.io.File;
import java.io.IOException;

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

        try {
            // 1. 先定位要截图的目标元素
            WebElement loginForm = driver.findElement(By.id("login-form"));
            // 2. 直接通过元素调用截图方法,仅截取该元素区域
            File srcFile = loginForm.getScreenshotAs(OutputType.FILE);
            // 3. 保存截图
            File destFile = new File("./screenshot/登录表单截图.png");
            FileUtils.copyFile(srcFile, destFile);
            System.out.println("元素截图已保存");
        } catch (IOException e) {
            System.out.println("元素截图失败:" + e.getMessage());
        }

        driver.quit();
    }
}
Python 代码示例
# Python版本 单个元素截图
from selenium import webdriver
from selenium.webdriver.common.by import By
import os

if not os.path.exists("./screenshot"):
    os.makedirs("./screenshot")

driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 1. 先定位要截图的目标元素
login_form = driver.find_element(By.ID, "login-form")
# 2. 直接通过元素调用截图方法
login_form.screenshot("./screenshot/登录表单截图.png")
print("元素截图已保存")

driver.quit()

核心用法 3:截图为 Base64 格式【适配测试报告,无需保存本地文件】

Java 代码示例
// Java版本 Base64格式截图
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

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

        // 1. 强转为截图接口
        TakesScreenshot screenshot = (TakesScreenshot) driver;
        // 2. 直接获取截图的Base64编码字符串
        String base64Img = screenshot.getScreenshotAs(OutputType.BASE64);
        // 3. 输出结果,可直接嵌入HTML测试报告中
        System.out.println("截图Base64编码:data:image/png;base64," + base64Img);

        driver.quit();
    }
}
Python 代码示例
# Python版本 Base64格式截图
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 直接获取截图的Base64编码
base64_img = driver.get_screenshot_as_base64()
# 可直接嵌入HTML测试报告中
print("截图Base64编码:data:image/png;base64," + base64_img)

driver.quit()

19. 怎么用 Selenium 往文本框里输入文本?

共有 3 种常用方法,适配不同的输入场景:

  1. sendKeys() - 最常用,直接输入
  2. Actions类 - 模拟键盘输入
  3. 剪贴板+Ctrl+V - 特殊场景使用
Java 代码示例
// Java版本 文本框输入示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.KeyEvent;

public class TextInputDemo {
    public static void main(String[] args) throws AWTException {
        WebDriver driver = new ChromeDriver();
        driver.get("https://www.example.com");
        WebElement usernameInput = driver.findElement(By.id("username"));

        // 方法1:sendKeys() 【最常用,99%的场景都用这个】
        usernameInput.sendKeys("test123456");

        // 方法2:Actions类模拟键盘输入,适配特殊加密输入框
        Actions actions = new Actions(driver);
        actions.moveToElement(usernameInput) // 定位到输入框
               .click() // 点击激活输入框
               .sendKeys("test123456") // 输入内容
               .perform(); // 执行动作

        // 方法3:剪贴板+Ctrl+V粘贴,适配无法直接输入的特殊输入框
        // 3.1 把要输入的文本放入系统剪贴板
        String inputText = "test123456";
        StringSelection stringSelection = new StringSelection(inputText);
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.setContents(stringSelection, null);
        // 3.2 激活输入框
        usernameInput.click();
        // 3.3 模拟Ctrl+V粘贴
        Robot robot = new Robot();
        robot.keyPress(KeyEvent.VK_CONTROL); // 按下Ctrl键
        robot.keyPress(KeyEvent.VK_V); // 按下V键
        robot.keyRelease(KeyEvent.VK_V); // 松开V键
        robot.keyRelease(KeyEvent.VK_CONTROL); // 松开Ctrl键

        driver.quit();
    }
}
Python 代码示例
# Python版本 文本框输入示例
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import pyperclip

driver = webdriver.Chrome()
driver.get("https://www.example.com")
username_input = driver.find_element(By.ID, "username")

# 方法1:send_keys() 【最常用,99%的场景都用这个】
username_input.send_keys("test123456")

# 清空输入框
# username_input.clear()

# 方法2:Actions类模拟键盘输入,适配特殊加密输入框
actions = ActionChains(driver)
actions.move_to_element(username_input)
actions.click()
actions.send_keys("test123456")
actions.perform()

# 方法3:剪贴板+Ctrl+V粘贴(需要pyperclip库)
input_text = "test123456"
pyperclip.copy(input_text)
username_input.click()
actions.key_down(Keys.CONTROL)
actions.send_keys("v")
actions.key_up(Keys.CONTROL)
actions.perform()

driver.quit()

20. 怎么判断一个元素是不是显示在屏幕上?

Selenium 提供了 3 个核心方法,用于判断元素的状态:

  1. isDisplayed() - 判断元素是否可见
  2. isEnabled() - 判断元素是否可操作
  3. isSelected() - 判断单选框/复选框是否被选中
Java 代码示例
// Java版本 元素状态判断示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class ElementStatusDemo {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        driver.get("https://www.example.com");
        WebElement submitButton = driver.findElement(By.id("submitBtn"));
        WebElement genderRadio = driver.findElement(By.id("gender-male"));

        // 1. isDisplayed():判断元素是否在页面上可见(非隐藏状态)
        boolean isDisplayed = submitButton.isDisplayed();
        System.out.println("元素是否可见:" + isDisplayed);

        // 2. isEnabled():判断元素是否可操作(比如按钮是否为灰色禁用状态)
        boolean isEnabled = submitButton.isEnabled();
        System.out.println("元素是否可点击:" + isEnabled);

        // 3. isSelected():判断单选框、复选框是否被选中
        boolean isSelected = genderRadio.isSelected();
        System.out.println("单选框是否被选中:" + isSelected);

        driver.quit();
    }
}
Python 代码示例
# Python版本 元素状态判断示例
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.example.com")
submit_button = driver.find_element(By.ID, "submitBtn")
gender_radio = driver.find_element(By.ID, "gender-male")

# 1. is_displayed():判断元素是否在页面上可见(非隐藏状态)
is_displayed = submit_button.is_displayed()
print("元素是否可见:" + str(is_displayed))

# 2. is_enabled():判断元素是否可操作(比如按钮是否为灰色禁用状态)
is_enabled = submit_button.is_enabled()
print("元素是否可点击:" + str(is_enabled))

# 3. is_selected():判断单选框、复选框是否被选中
is_selected = gender_radio.is_selected()
print("单选框是否被选中:" + str(is_selected))

driver.quit()

21. 怎么用 linkText 点击超链接?

通过 By.linkText() 可精准匹配超链接的完整文本并点击,通过 By.partialLinkText() 可模糊匹配超链接的部分文本。

Java 代码示例
// Java版本 超链接点击示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class LinkTextClickDemo {
    public static void main(String[] args) throws InterruptedException {
        WebDriver driver = new ChromeDriver();
        driver.get("https://www.example.com");

        // 方法1:linkText 精准匹配超链接的完整文本,点击【推荐用法】
        driver.findElement(By.linkText("联系我们")).click();
        Thread.sleep(1000);

        // 方法2:partialLinkText 模糊匹配超链接的部分文本,适配长文本超链接
        // 比如超链接完整文本是"2024年最新用户服务协议与隐私政策",只需输入部分文本即可匹配
        driver.findElement(By.partialLinkText("用户服务协议")).click();

        driver.quit();
    }
}
Python 代码示例
# Python版本 超链接点击示例
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 方法1:linkText 精准匹配超链接的完整文本,点击【推荐用法】
driver.find_element(By.LINK_TEXT, "联系我们").click()
time.sleep(1)

# 方法2:partialLinkText 模糊匹配超链接的部分文本,适配长文本超链接
# 比如超链接完整文本是"2024年最新用户服务协议与隐私政策",只需输入部分文本即可匹配
driver.find_element(By.PARTIAL_LINK_TEXT, "用户服务协议").click()

driver.quit()

22. Selenium 中的三大等待机制是什么?区别是什么?

核心有三种等待方式:

1. Thread.sleep() 【强制等待】

  • 特点:强制等待指定时间,无论元素是否加载完成都必须等够时间
  • 缺点:效率极低,时间不好把控,用多了脚本变慢
  • 适用场景:仅用于演示,工作中不推荐使用
  • 示例Thread.sleep(3000);

2. 隐式等待(Implicit Wait)

  • 特点:设置一个全局等待时间,找元素时如果没找到,会在指定时间内持续等待,超时后抛异常
  • 作用范围:对整个 driver 生命周期有效,全局生效
  • 代码示例
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

3. 显式等待(Explicit Wait)【工作最常用】

  • 特点:针对特定元素设置等待条件,满足条件就立刻执行,不等待满时间
  • 优势:灵活、高效,只针对需要等待的元素,避免全局等待
  • 常用条件
    • elementToBeClickable() - 元素可点击
    • visibilityOfElementLocated() - 元素可见
    • presenceOfElementLocated() - 元素存在
    • textToBePresentInElement() - 元素包含指定文本
Java 代码示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;

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

        // 创建显式等待对象,最多等待10秒
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

        // 等待元素可点击后再点击
        WebElement loginBtn = wait.until(
            ExpectedConditions.elementToBeClickable(By.id("loginBtn"))
        );
        loginBtn.click();

        driver.quit();
    }
}
Python 代码示例
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 初始化驱动
driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 创建显式等待对象,最多等待10秒
wait = WebDriverWait(driver, 10)

# 等待元素可点击后再点击
login_btn = wait.until(
    EC.element_to_be_clickable((By.ID, "loginBtn"))
)
login_btn.click()

# 其他常用等待条件示例:
# 等待元素可见
# wait.until(EC.visibility_of_element_located((By.ID, "username")))

# 等待元素存在
# wait.until(EC.presence_of_element_located((By.ID, "password")))

# 等待元素包含指定文本
# wait.until(EC.text_to_be_present_in_element((By.ID, "message"), "登录成功"))

driver.quit()

4. FluentWait(流畅等待)

  • 特点:显式等待的高级版,可以设置轮询频率和忽略特定异常,如元素不存在异常
  • 代码示例
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;

import java.time.Duration;

Wait<WebDriver> fluentWait = new FluentWait<>(driver)
    .withTimeout(Duration.ofSeconds(30))           // 总超时时间
    .pollingEvery(Duration.ofMillis(500))           // 每500毫秒检查一次
    .ignoring(NoSuchElementException.class);         // 忽略找不到元素异常

三大等待的区别总结

等待方式效率灵活性推荐度适用场景
Thread.sleep最低最低❌ 不推荐仅演示用
隐式等待中等⚠️ 谨慎使用简单页面,元素加载稳定
显式等待最高最高✅ 强烈推荐所有工作场景

面试回答技巧:先讲显式等待是工作首选,再对比隐式和 sleep 的区别,最后给出代码示例。


23. 怎么处理下拉框(Select)?

Selenium 提供 Select 类专门处理下拉框,适用于 <select> 标签创建的标准下拉框。

处理步骤说明

  1. 首先定位到 <select> 标签元素
  2. 将元素包装成 Select 对象
  3. 使用 Select 类提供的方法进行选择操作
  4. 支持三种选择方式:索引、value属性、可见文本

Java 代码示例

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;

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

        // 1. 先定位下拉框元素
        WebElement selectElement = driver.findElement(By.id("city"));
        Select citySelect = new Select(selectElement);

        // 2. 三种选择方式
        // 方式1:通过索引选择(从0开始)
        citySelect.selectByIndex(1);

        // 方式2:通过value属性选择
        citySelect.selectByValue("beijing");

        // 方式3:通过可见文本选择【最常用】
        citySelect.selectByVisibleText("北京市");

        // 3. 获取选中的选项
        WebElement selectedOption = citySelect.getFirstSelectedOption();
        System.out.println("当前选中:" + selectedOption.getText());

        // 4. 取消选中(仅多选下拉框可用)
        citySelect.deselectAll();

        driver.quit();
    }
}

Python 代码示例

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select

# 初始化驱动
driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 1. 先定位下拉框元素
select_element = driver.find_element(By.ID, "city")
city_select = Select(select_element)

# 2. 三种选择方式
# 方式1:通过索引选择(从0开始)
city_select.select_by_index(1)

# 方式2:通过value属性选择
city_select.select_by_value("beijing")

# 方式3:通过可见文本选择【最常用】
city_select.select_by_visible_text("北京市")

# 3. 获取选中的选项
selected_option = city_select.first_selected_option
print("当前选中:" + selected_option.text)

# 4. 取消选中(仅多选下拉框可用)
city_select.deselect_all()

driver.quit()

24. 怎么处理弹窗(Alert/Confirm/Prompt)?

Selenium 用 Alert 接口处理 JS 原生弹窗,主要包括三种类型:

  • Alert:只有确定按钮的警告弹窗
  • Confirm:有确定和取消两个按钮的确认弹窗
  • Prompt:可以输入文本的提示弹窗

处理步骤说明

  1. 先触发弹窗出现(点击按钮等操作)
  2. 使用 driver.switchTo().alert() 切换到弹窗
  3. 根据弹窗类型执行相应操作(获取文本、点击确定/取消、输入文本等)

Java 代码示例

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

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

        // 点击按钮触发弹窗
        driver.findElement(By.id("alertBtn")).click();

        // 切换到弹窗对象
        Alert alert = driver.switchTo().alert();

        // 1. 获取弹窗文本
        String alertText = alert.getText();
        System.out.println("弹窗内容:" + alertText);

        // 2. 点击确定按钮
        alert.accept();

        // 3. 点击取消按钮(仅Confirm/Prompt可用)
        // alert.dismiss();

        // 4. Prompt弹窗输入文本(仅Prompt可用)
        // alert.sendKeys("输入内容");

        driver.quit();
    }
}

Python 代码示例

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoAlertPresentException

# 初始化驱动
driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 点击按钮触发弹窗
driver.find_element(By.ID, "alertBtn").click()

try:
    # 切换到弹窗对象
    alert = driver.switch_to.alert

    # 1. 获取弹窗文本
    alert_text = alert.text
    print("弹窗内容:" + alert_text)

    # 2. 点击确定按钮
    alert.accept()

    # 3. 点击取消按钮(仅Confirm/Prompt可用)
    # alert.dismiss()

    # 4. Prompt弹窗输入文本(仅Prompt可用)
    # alert.send_keys("输入内容")

except NoAlertPresentException:
    print("没有弹窗出现")

driver.quit()

25. 怎么处理多窗口/多标签页切换?

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.Set;

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

        // 获取当前窗口句柄
        String originalWindow = driver.getWindowHandle();

        // 点击打开新窗口的按钮
        driver.findElement(By.id("newWindowBtn")).click();

        // 获取所有窗口句柄
        Set<String> allWindows = driver.getWindowHandles();

        // 遍历切换到新窗口
        for (String window : allWindows) {
            if (!window.equals(originalWindow)) {
                driver.switchTo().window(window);
                break;
            }
        }

        // 在新窗口操作
        System.out.println("新窗口标题:" + driver.getTitle());

        // 切回原窗口
        driver.switchTo().window(originalWindow);

        driver.quit();
    }
}

26. 怎么处理鼠标和键盘操作(Actions 类)?

import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class ActionsDemo {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        driver.get("https://www.example.com");
        Actions actions = new Actions(driver);

        WebElement element = driver.findElement(By.id("target"));

        // 1. 鼠标悬停
        actions.moveToElement(element).perform();

        // 2. 右键点击
        actions.contextClick(element).perform();

        // 3. 双击
        actions.doubleClick(element).perform();

        // 4. 拖拽(从source拖到target)
        WebElement source = driver.findElement(By.id("source"));
        WebElement target = driver.findElement(By.id("target"));
        actions.dragAndDrop(source, target).perform();

        // 5. 模拟键盘操作(Ctrl+A全选)
        actions.sendKeys(element, "test")
               .keyDown(Keys.CONTROL)
               .sendKeys("a")
               .keyUp(Keys.CONTROL)
               .perform();

        driver.quit();
    }
}

27. 怎么处理文件上传?

方式1:input标签直接sendKeys(推荐)

如果是 <input type="file"> 标签,直接用 sendKeys 传文件路径:

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

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

        // 直接给input标签发送文件绝对路径
        driver.findElement(By.id("fileInput"))
              .sendKeys("C:\\Users\\test\\Desktop\\test.jpg");

        driver.quit();
    }
}

方式2:非input标签用AutoIt/Robot(复杂场景)


28. 怎么操作 Cookie?

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

import java.util.Set;

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

        // 1. 添加Cookie
        Cookie cookie = new Cookie("username", "test123");
        driver.manage().addCookie(cookie);

        // 2. 获取所有Cookie
        Set<Cookie> cookies = driver.manage().getCookies();
        for (Cookie c : cookies) {
            System.out.println(c.getName() + ": " + c.getValue());
        }

        // 3. 根据name获取Cookie
        Cookie myCookie = driver.manage().getCookieNamed("username");

        // 4. 删除指定Cookie
        driver.manage().deleteCookieNamed("username");

        // 5. 删除所有Cookie
        driver.manage().deleteAllCookies();

        driver.quit();
    }
}

29. Selenium 常见异常有哪些?

异常名称触发原因解决方法
NoSuchElementException找不到元素检查定位器、加等待、检查iframe
TimeoutException等待超时延长等待时间、检查元素是否存在
StaleElementReferenceException元素已过期(DOM刷新)重新定位元素
ElementNotVisibleException元素不可见检查display属性、等待可见
ElementNotInteractableException元素不可交互检查是否被遮挡、是否禁用
NoAlertPresentException弹窗不存在检查是否真的弹出了弹窗
NoSuchFrameExceptioniframe不存在检查iframe的id/name是否正确
WebDriverException驱动相关问题检查驱动版本与浏览器匹配

30. Selenium 4 有哪些新特性?

  1. 相对定位器(Relative Locators)
    • above() - 某个元素上方
    • below() - 某个元素下方
    • toLeftOf() - 某个元素左侧
    • toRightOf() - 某个元素右侧
    • near() - 某个元素附近
  2. 打开新标签页/窗口
    import org.openqa.selenium.WindowType;
    
    driver.switchTo().newWindow(WindowType.TAB);
    driver.switchTo().newWindow(WindowType.WINDOW);
    
  3. 元素截图
    WebElement element = driver.findElement(By.id("logo"));
    File screenshot = element.getScreenshotAs(OutputType.FILE);
    
  4. 优化的 DevTools 协议支持
  5. 弃用了部分旧 API(如 findElementByName 等)

31. 自动化测试的局限性是什么?

  1. 不能完全替代手工测试:UI体验、易用性还需要人来判断
  2. 维护成本高:页面频繁变动时,脚本维护工作量大
  3. 初期投入大:需要搭建框架、编写脚本,前期耗时较长
  4. 对测试人员要求高:需要懂代码、懂框架
  5. 不是所有功能都适合自动化:一次性、迭代快的功能不适合

32. 怎么提高自动化测试脚本的稳定性?

  1. 优先使用唯一标识:id > name > css > xpath
  2. 避免绝对路径:用相对 XPath,多用属性定位
  3. 合理使用显式等待:不用 sleep,不用隐式等待
  4. 封装公共方法:点击、输入、等待统一封装
  5. 降低元素依赖:减少元素之间的强依赖关系
  6. 数据与脚本分离:测试数据单独管理
  7. 异常处理和重试机制:遇到异常自动重试
  8. 定期清理测试数据:保持测试环境干净

33. Java 执行 Selenium 和 Python 执行 Selenium 有什么区别?

核心功能相同:二者都使用 Selenium WebDriver 的 API,能实现完全相同的浏览器自动化操作。主要区别在于编程语言本身及生态:

特性Java + SeleniumPython + Selenium
语言特性强类型、静态语言,代码严谨但相对冗长弱类型、动态语言,语法简洁,编写速度快
学习曲线需理解面向对象、类型系统,入门门槛略高语法接近自然语言,新手友好,上手更快
开发效率代码量通常更大,编译步骤稍慢代码简洁,无需编译,快速原型开发首选
生态与库支持成熟企业级生态(JUnit/TestNG, Maven/Gradle)丰富的数据科学库(Pandas, NumPy),适合爬虫
应用场景大型企业级测试框架,需要强类型保障的长期项目快速脚本、爬虫、中小型测试项目,AI结合场景
执行速度JVM优化后通常更快略慢于Java,但日常操作差异不明显

如何选择?

  • 选 Java:团队已有Java基础或企业级测试框架(如TestNG),项目需要强类型检查和长期维护
  • 选 Python:快速实现需求(写脚本、爬虫、小型测试),团队熟悉Python或需要结合数据分析/机器学习

总结:功能无差距,差异在语言。Python适合敏捷开发和初学者,Java适合大型企业级项目。


34. 如果页面上的元素用隐性等待和显性等待都没等到元素加载出来,请问要怎么办?

排查思路(按优先级):

1)验证元素是否存在(基础检查)
  • 手动验证:在浏览器开发者工具(F12)中手动执行定位表达式,确认元素是否存在
  • 检查时机:在等待后添加 driver.page_source 输出当前HTML源码,检查元素是否被加载
2)定位策略失效的排查(核心原因)
问题类型解决方案
过时定位器检查元素属性(ID/Class)是否随页面刷新/重渲染变化(常见于SPA应用)
动态生成元素使用更稳定的定位方式:XPath结合文本、父节点关系,或CSS属性选择器
元素在iframe中切换至iframe:driver.switch_to.frame("frame_id/name/index") 后再定位
元素在Shadow DOM使用 driver.execute_script() 穿透Shadow Root(需JS脚本辅助)
3)等待条件不匹配

显性等待的常见误用:等待"存在" ≠ 等待"可交互"

# ❌ 错误:元素存在但可能被遮挡/不可点击
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "btn"))
)

# ✅ 正确:确保元素可操作
WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "btn"))
)
4)页面状态异常(进阶排查)
场景解决方案
页面未完全加载设置页面加载策略:options.page_load_strategy = 'eager'(放弃加载图片等资源)
AJAX/JS未完成自定义等待条件:检查JS变量或特定元素状态
弹窗遮挡元素关闭弹窗:driver.switch_to.alert.dismiss() 或执行JS移除遮挡层
新窗口/标签页切换窗口句柄:driver.switch_to.window(driver.window_handles[1])
5)终极方案:自定义等待条件

当内置条件不满足时,用JavaScript轮询目标状态:

from selenium.webdriver.support.ui import WebDriverWait

def wait_for_element_state(driver, selector, state="visible", timeout=10):
    script = """
        const el = document.querySelector(arguments[0]);
        if (arguments[1] === 'visible') 
            return el && getComputedStyle(el).display !== 'none';
        // 可扩展其他状态:enabled, hasText...
    """
    return WebDriverWait(driver, timeout).until(
        lambda d: d.execute_script(script, selector, state)
    )

# 使用示例
wait_for_element_state(driver, "#dynamicElement", "visible")
6)环境与执行问题
问题解决方案
浏览器窗口太小最大化窗口:driver.maximize_window()
资源加载超时增加页面加载超时:driver.set_page_load_timeout(60)
浏览器驱动版本不匹配使用 webdriver-manager 自动匹配驱动
偶发性失败添加重试机制(如 tenacity 库)

35. XPath查找元素时间过长的优化方法

优化思路:

  1. 优先用CSS选择器替代
  2. 限定搜索范围(使用父元素上下文)
  3. 避免使用 //contains()
  4. 指定标签类型(避免 * 通配符)
  5. 禁用不必要的全局等待

具体优化方法:

1)优化 XPath 表达式

避免低效语法,使用精准定位:

低效写法高效替代方案效率提升原理
//div//span//div/span减少中间节点扫描
//*[@class='btn']//button[@class='btn']限定标签类型减少搜索范围
//div[contains(@class,'icon')]//div[@class='user-icon']避免contains等函数计算
2)缩小搜索范围

从父节点开始查找:

# 先快速定位父元素(用ID/CSS)
parent = driver.find_element(By.ID, "form-container")
# 再在父元素内用XPath(注意开头的点.)
child = parent.find_element(By.XPATH, ".//input[@name='email']")

利用最近定位点:

# 已定位元素作为上下文
known_element = driver.find_element(By.CSS_SELECTOR, "#sidebar")
target = known_element.find_element(By.XPATH, "following-sibling::div[1]")
3)改用 CSS 选择器

CSS 选择器通常比 XPath 快 5-10 倍(浏览器原生优化):

# XPath 低效定位
# //div[@class='container']/ul/li[3]/a

# CSS 高效替代
driver.find_element(By.CSS_SELECTOR, "div.container > ul > li:nth-child(3) > a")
4)优化等待策略

精准显性等待:

# 只等待必要的最小元素出现
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "essential_element"))
)
# 后续再查找复杂XPath
element = driver.find_element(By.XPATH, "//div[...]")

禁用隐性等待:

# 复杂查找前关闭全局等待
driver.implicitly_wait(0)  # 临时禁用
try:
    element = driver.find_element(By.XPATH, complex_xpath)
finally:
    driver.implicitly_wait(10)  # 恢复
5)处理动态内容策略

分阶段定位:

# 第一阶段:等待容器加载
container = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "dynamic-container"))
)
# 第二阶段:在容器内查找
items = container.find_elements(By.XPATH, ".//li[contains(@class, 'item')]")

直接 DOM 访问:

# 通过JS直接获取元素(比XPath快3-5倍)
script = "return document.querySelector('div.container > ul > li:nth-child(2)');"
element = driver.execute_script(script)

36. 自动化测试的使用场景?

适合自动化测试的场景:

  1. 需求稳定,不会频繁变更
  2. 研发和测试周期长,需要频繁执行回归测试
  3. 需要在多种平台上重复运行相同测试的场景
  4. 某些测试项目,通过手工测试无法实现,或者手工成本太高
  5. 被测软件的开发较为规范,能够保证系统的可测试性

37. 自动化测试发现BUG多吗?

不多。因为之前项目组是把已经测试通过的基本功能再进行自动化脚本编写和在后续版本执行自动化测试。

UI自动化的目的不是为了发现多少Bug,主要是为了:

  • 减轻重复的基础操作
  • 线上监控的作用
  • 保证已经测试通过的功能在新版本更新后没有问题

38. 自动化测试有误报过bug吗?产生误报怎么办?

误报过。有时候自动化测试报告中显示发现了bug,实际去通过手工测试去确认又不存在该bug。

误报原因:

  1. 元素定位不稳定,需要尽量提高脚本的稳定性
  2. 开发更新了页面但是测试没有及时更新维护

解决方案:

  • 优化定位策略,使用更稳定的定位方式
  • 及时维护脚本,跟随页面变更同步更新
  • 添加重试机制,偶发性失败自动重试
  • 加强与开发的沟通,提前了解页面变更计划

39. 自动化测试过程中,你遇到了哪些问题,是如何解决的?

根据项目实际经验回答,常见问题:

  1. 频繁地变更页面,经常要修改页面对象类里面的代码
    • 解决:使用POM模式,集中管理元素定位
  2. 自动化测试偶尔出现过误报
    • 解决:优化定位、添加重试、加强维护
  3. 自动化测试结果出现覆盖的情况
    • 解决:Jenkins根据时间建立文件夹
  4. 自动化测试代码维护比较麻烦
    • 解决:采用POM模式,分层设计,提高可维护性

40. 在执行脚本过程,如何实现当前元素高亮显示?

主要是用 driver.execute_script() 方法,利用 JavaScript 去修改当前元素的边框样式来到达高亮显示的效果。

element = driver.find_element(By.ID, "target")
# 高亮元素(红色边框)
driver.execute_script("arguments[0].style.border='3px solid red';", element)

41. 在日历这种web表单你是如何处理的?

首先要分析当前网页使用日历插件的前端代码:

  1. 看看能不能通过元素定位,点击日期实现
  2. 如果不能,可能需要借助 JavaScript
  3. 有些日历控件是一个文本输入框,可以直接 send_keys() 方法来实现传入一个时间的数据

42. 关闭浏览器中 quit 和 close 的区别?

操作说明适用场景
close()关闭你当前聚焦的 tab 页面执行用例过程中关闭某一个页面
quit()关闭全部浏览器 tab 页面,并退出浏览器 session结束测试之前的操作

43. Selenium 中如何保证操作元素的成功率?

  1. 添加元素智能等待时间driver.implicitly_wait(30)
  2. 添加强制等待时间time.sleep()(不推荐,仅临时调试用)
  3. try 方式进行多种定位方式尝试:id、name、class、xpath、css selector 不同方式进行定位,如果第一种失败可以自动尝试第二种

44. 在 Selenium 自动化测试中,你一般完成什么类型的测试?自动化覆盖率?

根据实际项目经验回答:

  • 主要测试类型:冒烟测试和回归测试
  • 回归测试重点:写一些功能稳定的场景,通过自动化手段去实现,节约测试时间
  • 覆盖率:自动化测试用例也是在不断的更新和迭代,没有刻意去统计,大概在 30%-40% 左右

45. 自动化测试中,Selenium 起什么作用?

Selenium 是一个核心工具,主要用于 Web 应用程序的自动化测试,显著提高测试的效率。

核心作用

  • 模拟真实用户操作浏览器
  • 自动执行测试用例(填写表单、点击按钮等)
  • 验证 Web 应用的功能、兼容性和稳定性

可执行的测试类型

  • 自动化 Web UI 测试
  • 跨浏览器测试
  • 自动化回归测试
  • 数据驱动测试

46. Selenium 的优势与局限性?

优势

优势说明
开源免费无需付费,社区支持强大
多语言支持支持 Python、Java、C#、JavaScript 等主流语言
真实浏览器测试直接操作浏览器,更贴近用户实际场景
强大的元素定位支持 ID、XPath、CSS Selector 等多种定位方式
集成 CI/CD可与 Jenkins、GitHub Actions 等工具集成,实现持续测试

局限性

局限性说明解决方案
无法测试非Web应用需结合 Appium(移动端)、WinAppDriver(桌面端)-
动态元素处理复杂使用显式等待(WebDriverWait)解决元素加载延迟问题
无图形验证码处理需手动绕过或使用第三方服务(如OCR识别)
执行速度较慢结合 Selenium Grid 并行测试,或使用无头模式(Headless)加速

47. Selenium 与其他工具相比?有什么优势?

Selenium 的跨浏览器兼容性、多语言支持和成熟的生态,使其在复杂企业级项目中仍是不可替代的工具。尽管新工具(如 Playwright)在速度和易用性上有优势,但 Selenium 的灵活性和稳定性依然是长期维护型项目的首选。


48. 自动化测试 Web 端页面,怎么去做性能测试?

Web 性能测试核心指标

  • 页面加载时间
  • 接口响应时间
  • 并发用户支持
  • 资源占用
  • 错误率

测试类型

测试类型目的实施方法
基准测试确定单用户正常性能用户循环执行核心流程
负载测试验证系统在目标并发下的表现逐步增加并发至业务峰值(如500TPS)
压力测试探测系统崩溃临界点持续增加负载直到错误率 > 5%
稳定性测试检查内存泄漏和长时间运行可靠性7x24 小时持续运行

49. 在自动化测试过程中,如何进一步提高用户体验?

从测试策略、工具优化、结果反馈等多个维度进行系统性改进:

从测试设计阶段提升用户体验

  • 用户旅程覆盖:自动化测试脚本应覆盖典型用户路径
  • 响应速度:通过 performance.timing 记录关键节点时间
  • 视觉一致性:使用视觉回归工具(如 Applitools)检测 UI 偏移
  • 无障碍访问:通过 axe-core 自动化检查 WCAG 合规性

优化测试执行体验

  • 智能等待机制:避免固定 SLEEP,采用动态等待策略
  • 失败场景人性化处理:自动截图 + 日志记录 / 失败自动重试
  • 跨设备/浏览器覆盖:使用 Selenium Grid 或 BrowserStack 自动测试不同设备

持续改进闭环

  • 用户反馈整合:将生产环境中的用户报错(如 Sentry 日志)自动转化为测试用例
  • A/B 测试验证:通过自动化脚本验证不同 UI 设计的转化率差异
  • 性能优化验证:在 CI 流水线中自动验证缓存策略、CDN 生效情况

50. Selenium 怎么与浏览器交互?

Selenium 与浏览器的交互是通过 浏览器驱动(WebDriver) 实现的,它基于 W3C WebDriver 协议,通过 HTTP 请求与浏览器通信,模拟用户操作。

核心流程
测试脚本 → WebDriver 驱动 → 浏览器实例 → 返回结果

组件说明

组件作用
Selenium Client Library提供编程语言接口(如 Python 的 selenium 包),将代码转换为 WebDriver 协议命令
Browser Driver浏览器专属驱动(如 ChromeDriver、GeckoDriver),接收指令并控制真实浏览器
浏览器实例实际执行操作的浏览器(Chrome/Firefox 等)

51. Selenium 交互的浏览器有什么不同?

浏览器ChromeFirefoxEdgeSafariInternet ExplorerOpera
驱动名称ChromeDriverGeckoDriverMSEdgeDriverSafariDriverIEDriverServerOperaDriver
官方支持✅ Google✅ Mozilla✅ Microsoft✅ Apple(仅MacOS)❌ 已停用❌ 社区维护
兼容性最佳,支持最新Web标准良好,但对部分CSS3/JavaScript支持滞后同Chrome(Chromium内核)仅限MacOS,对H5特性支持一般仅支持旧版Web标准-
性能对比中等快(同Chrome)极慢-
Headless支持-

如何选择浏览器?

  • 开发/调试:Chrome(DevTools强大)
  • 生产测试:Chrome + Firefox(覆盖主流用户)
  • 企业环境:Edge(兼容Windows策略)
  • Mac用户:Safari(验证Apple生态兼容性)

52. XPath 和 CSS 定位有什么不同?

特性XPathCSS Selector
基本格式基于XML路径语法(如 / 和 //)基于CSS样式规则(如 . 和 #)
按ID定位//*[@id='username']#username
按Class定位//*[contains(@class,'btn')].btn-primary
按属性定位//input[@name='email']input[name='email']
文本匹配//button[text()='Submit']不支持(需结合JavaScript)
向上遍历✅ 支持(如 //input/.. 找父节点)❌ 不支持
复杂逻辑✅ 支持 and、or有限

适用场景推荐

场景推荐选择理由
按ID/Class快速定位CSS语法简洁(如 #submit-btn//*[@id='submit-btn'] 更直观)
需要匹配文本内容XPathCSS无法直接定位文本
动态生成的属性(部分匹配)两者均可XPath 的 contains() 或 CSS 的 *=
复杂DOM层级遍历XPath支持相对路径和轴(如 following-sibling::

53. 各种定位元素如:id,name,text,xpath 要怎么选择?

黄金法则:ID > Name > CSS Selector > XPath > 其他 [速度排名(从快到慢)]

  • 动态元素:用 contains、starts-with 等部分匹配
  • 团队协作:统一约定定位策略(如优先用 data-testid)
  • 终极目标:在稳定性、可读性和性能之间找到平衡!

54. 自动化测试 web 页面,怎么确保修改后的测试用例可以覆盖到关键的测试点?

Checklist

  • 关键测试点是否与需求文档一致?
  • 修改后的用例是否通过覆盖率工具验证?
  • 是否包含单元、集成、E2E 多层测试?
  • 断言是否覆盖成功/失败场景?
  • 是否加入回归测试套件?

具体方法

  1. 需求与变更分析
    • 明确关键测试点:基于需求文档或用户故事,列出核心功能、高风险模块
    • 变更影响分析:修改测试用例前,通过代码/需求变更记录识别受影响的功能模块
  2. 测试用例设计验证
    • 检查测试覆盖率:使用工具(如 Istanbul/JaCoCo)统计代码覆盖率
    • 等价类与边界值:针对输入参数,明确有效/无效等价类和边界值
  3. 自动化测试层级的覆盖
    • 单元测试:覆盖工具函数、组件逻辑
    • 集成测试:验证组件交互
    • E2E 测试:覆盖用户完整流程
  4. 动态验证手段
    • 断言关键结果:每个测试用例需包含明确的断言
    • 自动化回归套件:将修改后的用例加入回归测试
  5. 辅助工具与文档
    • 测试用例管理工具:使用 TestRail/Zephyr 标记用例与需求的关联
    • 可视化覆盖率报告:生成并监控覆盖率报告
  6. 监控与反馈
    • 失败用例分析:定期检查自动化测试失败日志,补充遗漏场景
    • 生产环境监控:通过 A/B 测试或实时监控验证新功能是否按预期运行
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐