
引言:自动化表格排序验证的重要性
在现代 web 应用程序中,表格是展示大量结构化数据的常用方式。用户经常需要根据特定列(如金额、日期、名称等)对数据进行升序或降序排序,以便快速查找和分析信息。作为自动化测试工程师,验证这些排序功能的正确性至关重要。本教程将详细指导您如何使用 java 和 selenium webdriver 自动化验证网页表格中的数值型数据排序。
1. 环境准备与 WebDriver 初始化
开始之前,请确保您的开发环境中已安装 Java JDK、Maven 或 Gradle(用于依赖管理),并下载了对应浏览器版本的 WebDriver 可执行文件(例如 ChromeDriver)。
首先,我们需要设置 WebDriver 并打开目标网页。
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TableSortVerification {
private static WebDriver driver;
private static WebDriverWait wait;
@BeforeClass
public static void setupWebDriver() {
// 设置 ChromeDriver 路径
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe"); // 请替换为您的 ChromeDriver 实际路径
driver = new ChromeDriver();
// 初始化显式等待,最大等待时间为10秒
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// 最大化浏览器窗口
driver.manage().window().maximize();
// 导航到登录页面
String baseUrl = "https://sakshingp.github.io/assignment/login.html";
driver.get(baseUrl);
}2. 用户登录与页面导航
在进行表格排序验证之前,通常需要先完成用户登录或导航到包含目标表格的页面。
@Test
public void verifyAmountTableSorting() {
// 1. 执行登录操作
System.out.println("执行登录操作...");
driver.findElement(By.id("username")).sendKeys("Lakshay");
driver.findElement(By.id("password")).sendKeys("Wingify");
driver.findElement(By.id("log-in")).click();
// 显式等待,直到进入主页,例如等待“Amount”列头可见
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("amount")));
System.out.println("登录成功,进入主页。");
// (可选) 滚动页面,确保所有元素可见
// JavascriptExecutor js = (JavascriptExecutor) driver;
// js.executeScript("window.scrollBy(0, document.body.scrollHeight)");3. 提取并清洗表格数据
这是核心步骤之一。我们需要从网页表格中提取数值,并进行必要的清洗和类型转换,以便后续的排序和比较。
立即学习“Java免费学习笔记(深入)”;
关键点:
- 定位表格元素: 使用 XPath 或 CSS 选择器定位到表格中所有“金额”列的单元格。
- 数据清洗: 移除货币符号(如 "USD")、千位分隔符(如 ",")和多余的空格。
- 数据类型转换: 将清洗后的字符串转换为 Double 或 BigDecimal 类型,以确保数值比较的准确性。直接对字符串进行排序可能导致 "100.00" 排在 "20.00" 之前。
/**
* 辅助方法:从页面提取并清洗金额数据
* @return 清洗并转换为 Double 类型的金额列表
*/
private List extractAndCleanAmounts() {
// 定位所有金额列的 span 元素
List amountElements = driver.findElementsByXPath("//*[@id=\"transactionsTable\"]/tbody/tr/td/span");
List cleanedAmounts = new ArrayList<>();
for (WebElement element : amountElements) {
String text = element.getText();
// 清洗字符串:移除 "USD", ",", " "
String cleanedText = text.replaceAll("USD", "")
.replaceAll(",", "")
.replaceAll(" ", "")
.trim();
// 转换为 Double 类型
try {
cleanedAmounts.add(Double.parseDouble(cleanedText));
} catch (NumberFormatException e) {
System.err.println("无法解析金额字符串: " + cleanedText + ",跳过此项。");
}
}
return cleanedAmounts;
} 4. 触发排序操作与等待页面更新
在验证排序之前,需要模拟用户点击排序按钮。点击后,页面内容通常会动态更新,因此需要等待页面完成更新。
// 2. 提取点击排序按钮前的原始数据(用于生成预期排序结果)
List initialAmounts = extractAndCleanAmounts();
System.out.println("初始金额数据: " + initialAmounts);
// 3. 点击“Amount”列头触发排序
System.out.println("点击 'Amount' 按钮触发排序...");
WebElement amountHeader = driver.findElement(By.id("amount"));
amountHeader.click();
// 显式等待,直到表格内容更新。
// 可以等待某个特定元素的状态变化,或者等待表格数据重新加载。
// 这里我们简单地等待 Amount 列头再次可见(暗示页面已稳定),
// 或者更健壮地等待某个表格行中的数据发生变化。
wait.until(ExpectedConditions.stalenessOf(amountHeader)); // 等待旧的元素变得“陈旧”
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("amount"))); // 等待新的元素或刷新后的元素可见
System.out.println("排序操作完成,等待页面更新。"); 5. 生成预期排序列表
预期排序列表是通过对原始提取数据进行程序化排序而得到的。这与页面上实际显示的排序结果进行比较。
// 4. 生成预期升序排序列表
List expectedSortedAscendingAmounts = new ArrayList<>(initialAmounts);
Collections.sort(expectedSortedAscendingAmounts); // 默认升序
System.out.println("预期升序排序结果: " + expectedSortedAscendingAmounts);
// 如果需要验证降序,可以这样生成预期降序列表:
// List expectedSortedDescendingAmounts = new ArrayList<>(initialAmounts);
// Collections.sort(expectedSortedDescendingAmounts, Collections.reverseOrder());
// System.out.println("预期降序排序结果: " + expectedSortedDescendingAmounts); 6. 验证实际与预期排序结果
最后一步是重新从页面提取排序后的数据(即实际结果),并将其与我们程序生成的预期排序列表进行比较。
// 5. 重新从页面提取实际排序后的数据
List actualSortedAmounts = extractAndCleanAmounts();
System.out.println("实际排序结果: " + actualSortedAmounts);
// 6. 断言:比较实际排序结果与预期升序结果
System.out.println("开始验证排序结果...");
Assert.assertEquals("表格金额未按预期升序排序!", expectedSortedAscendingAmounts, actualSortedAmounts);
System.out.println("表格金额已成功按升序排序!");
}
@AfterClass
public static void tearDown() {
if (driver != null) {
driver.quit(); // 关闭浏览器
System.out.println("浏览器已关闭。");
}
}
} 完整示例代码
以下是整合所有步骤的完整 JUnit 测试类:
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TableSortVerification {
private static WebDriver driver;
private static WebDriverWait wait;
@BeforeClass
public static void setupWebDriver() {
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe"); // 请替换为您的 ChromeDriver 实际路径
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.manage().window().maximize();
String baseUrl = "https://sakshingp.github.io/assignment/login.html";
driver.get(baseUrl);
}
@Test
public void verifyAmountTableSorting() {
// 1. 执行登录操作
System.out.println("执行登录操作...");
driver.findElement(By.id("username")).sendKeys("Lakshay");
driver.findElement(By.id("password")).sendKeys("Wingify");
driver.findElement(By.id("log-in")).click();
// 显式等待,直到进入主页,例如等待“Amount”列头可见
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("amount")));
System.out.println("登录成功,进入主页。");
// 2. 提取点击排序按钮前的原始数据
List initialAmounts = extractAndCleanAmounts();
System.out.println("初始金额数据: " + initialAmounts);
// 3. 点击“Amount”列头触发排序
System.out.println("点击 'Amount' 按钮触发排序...");
WebElement amountHeader = driver.findElement(By.id("amount"));
amountHeader.click();
// 显式等待,直到表格内容更新。
// 这里等待旧的元素变得“陈旧”,然后等待新的元素或刷新后的元素可见,以确保页面已稳定。
wait.until(ExpectedConditions.stalenessOf(amountHeader));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("amount")));
System.out.println("排序操作完成,等待页面更新。");
// 4. 生成预期升序排序列表
List expectedSortedAscendingAmounts = new ArrayList<>(initialAmounts);
Collections.sort(expectedSortedAscendingAmounts); // 默认升序
System.out.println("预期升序排序结果: " + expectedSortedAscendingAmounts);
// 如果需要验证降序,可以这样生成预期降序列表:
// List expectedSortedDescendingAmounts = new ArrayList<>(initialAmounts);
// Collections.sort(expectedSortedDescendingAmounts, Collections.reverseOrder());
// System.out.println("预期降序排序结果: " + expectedSortedDescendingAmounts);
// 5. 重新从页面提取实际排序后的数据
List actualSortedAmounts = extractAndCleanAmounts();
System.out.println("实际排序结果: " + actualSortedAmounts);
// 6. 断言:比较实际排序结果与预期升序结果
System.out.println("开始验证排序结果...");
Assert.assertEquals("表格金额未按预期升序排序!", expectedSortedAscendingAmounts, actualSortedAmounts);
System.out.println("表格金额已成功按升序排序!");
}
/**
* 辅助方法:从页面提取并清洗金额数据
* @return 清洗并转换为 Double 类型的金额列表
*/
private List extractAndCleanAmounts() {
// 定位所有金额列的 span 元素
List amountElements = driver.findElementsByXPath("//*[@id=\"transactionsTable\"]/tbody/tr/td/span");
List cleanedAmounts = new ArrayList<>();
for (WebElement element : amountElements) {
String text = element.getText();
// 清洗字符串:移除 "USD", ",", " "
String cleanedText = text.replaceAll("USD", "")
.replaceAll(",", "")
.replaceAll(" ", "")
.trim();
// 转换为 Double 类型
try {
cleanedAmounts.add(Double.parseDouble(cleanedText));
} catch (NumberFormatException e) {
System.err.println("无法解析金额字符串: " + cleanedText + ",跳过此项。");
}
}
return cleanedAmounts;
}
@AfterClass
public static void tearDown() {
if (driver != null) {
driver.quit(); // 关闭浏览器
System.out.println("浏览器已关闭。");
}
}
} 注意事项与最佳实践
- 数据类型处理: 对于数值型数据(尤其是金融数据),务必将其转换为 Double 或 BigDecimal 进行比较。直接对字符串进行排序(例如 "100" 在 "20" 之前)会导致错误的结果。
- 显式等待(Explicit Waits): 避免使用 Thread.sleep() 或 CountDownLatch 等硬编码的等待方式。WebDriverWait 结合 ExpectedConditions 可以更智能、更稳定地等待页面元素状态变化,提高测试的健壮性。
- 健壮的定位器: 优先使用唯一的 ID 或 CSS 选择器。当没有 ID 时,使用稳定且不易变化的 CSS 选择器。XPath 虽然强大,但如果结构变化频繁,维护成本较高。
- 国际化处理: 如果您的应用程序支持多种语言或地区,请注意不同地区数字格式(例如,逗号作为小数点分隔符)可能带来的影响。在清洗数据时需要考虑这些差异。
- 错误处理与日志记录: 在数据解析等可能出错的地方添加 try-catch 块,并输出有意义的日志信息,有助于调试和问题排查。
- 验证升序/降序: 如果需要验证降序排序,在生成预期列表时使用 Collections.sort(list, Collections.reverseOrder());。在实际应用中,您可能需要根据点击排序按钮的次数或表格列头的状态来判断当前是升序还是降序。
总结
通过本文的详细教程,您应该已经掌握了使用 Java Selenium 验证网页表格数据排序的核心方法。从 WebDriver 初始化到数据提取、清洗、排序和最终验证,每一步都至关重要。遵循最佳实践,










