18. 字符串操作
在Java+Selenium自动化测试中,字符串操作是非常重要的技能。我们经常需要处理从网页元素中获取的文本、验证文本内容、格式化数据等。本章将详细介绍在自动化测试中常用的字符串操作方法。
目录
1. 获取元素文本内容
1.1 getText() 方法
getText() 是Selenium中最常用的获取元素文本内容的方法。
基本语法
WebElement element = driver.findElement(By.id("elementId"));
String text = element.getText();
详细示例
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class TextOperationTest {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.com");
// 获取标题文本
WebElement title = driver.findElement(By.tagName("h1"));
String titleText = title.getText();
System.out.println("页面标题: " + titleText);
// 获取段落文本
WebElement paragraph = driver.findElement(By.className("content"));
String paragraphText = paragraph.getText();
System.out.println("段落内容: " + paragraphText);
// 获取按钮文本
WebElement button = driver.findElement(By.id("submitBtn"));
String buttonText = button.getText();
System.out.println("按钮文本: " + buttonText);
} finally {
driver.quit();
}
}
}
1.2 getAttribute() 方法获取属性值
有时候我们需要获取元素的属性值,而不是显示的文本。
// 获取input元素的value属性
WebElement inputField = driver.findElement(By.id("username"));
String inputValue = inputField.getAttribute("value");
// 获取链接的href属性
WebElement link = driver.findElement(By.tagName("a"));
String linkUrl = link.getAttribute("href");
// 获取图片的src属性
WebElement image = driver.findElement(By.tagName("img"));
String imageSrc = image.getAttribute("src");
// 获取元素的class属性
String className = element.getAttribute("class");
// 获取自定义属性
String customAttr = element.getAttribute("data-id");
1.3 getCssValue() 方法获取CSS属性值
// 获取元素的颜色
String color = element.getCssValue("color");
// 获取字体大小
String fontSize = element.getCssValue("font-size");
// 获取背景色
String backgroundColor = element.getCssValue("background-color");
System.out.println("元素颜色: " + color);
System.out.println("字体大小: " + fontSize);
System.out.println("背景色: " + backgroundColor);
2. 字符串基本操作
2.1 字符串长度和空值检查
public class StringBasicOperations {
// 检查字符串是否为空或null
public static boolean isStringEmpty(String str) {
return str == null || str.trim().isEmpty();
}
// 安全获取字符串长度
public static int getStringLength(String str) {
return str == null ? 0 : str.length();
}
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.com");
WebElement element = driver.findElement(By.id("message"));
String text = element.getText();
// 检查文本是否为空
if (isStringEmpty(text)) {
System.out.println("元素文本为空");
} else {
System.out.println("文本长度: " + getStringLength(text));
System.out.println("文本内容: " + text);
}
} finally {
driver.quit();
}
}
}
2.2 字符串去除空格和格式化
// 去除首尾空格
String trimmedText = text.trim();
// 去除所有空格
String noSpaceText = text.replaceAll("\s+", "");
// 去除多余空格,只保留单个空格
String singleSpaceText = text.replaceAll("\s+", " ").trim();
// 转换为大写
String upperCaseText = text.toUpperCase();
// 转换为小写
String lowerCaseText = text.toLowerCase();
// 首字母大写
String capitalizedText = text.substring(0, 1).toUpperCase() +
text.substring(1).toLowerCase();
System.out.println("原始文本: '" + text + "'");
System.out.println("去除空格: '" + trimmedText + "'");
System.out.println("无空格: '" + noSpaceText + "'");
System.out.println("单空格: '" + singleSpaceText + "'");
System.out.println("大写: " + upperCaseText);
System.out.println("小写: " + lowerCaseText);
System.out.println("首字母大写: " + capitalizedText);
2.3 字符串截取和替换
public class StringManipulation {
public static void demonstrateStringOperations() {
String originalText = "Welcome to Selenium Automation Testing";
// 字符串截取
String substring1 = originalText.substring(0, 7); // "Welcome"
String substring2 = originalText.substring(11, 19); // "Selenium"
String substring3 = originalText.substring(20); // "Automation Testing"
// 字符串替换
String replacedText1 = originalText.replace("Selenium", "WebDriver");
String replacedText2 = originalText.replaceAll("\s+", "_");
String replacedText3 = originalText.replaceFirst("e", "E");
// 输出结果
System.out.println("原始文本: " + originalText);
System.out.println("截取0-7: " + substring1);
System.out.println("截取11-19: " + substring2);
System.out.println("从20开始: " + substring3);
System.out.println("替换Selenium: " + replacedText1);
System.out.println("空格替换为下划线: " + replacedText2);
System.out.println("替换第一个e: " + replacedText3);
}
}
3. 字符串比较和验证
3.1 基本字符串比较
public class StringComparison {
// 精确比较(区分大小写)
public static boolean exactMatch(String actual, String expected) {
return actual != null && actual.equals(expected);
}
// 忽略大小写比较
public static boolean caseInsensitiveMatch(String actual, String expected) {
return actual != null && actual.equalsIgnoreCase(expected);
}
// 包含检查
public static boolean contains(String actual, String expected) {
return actual != null && actual.contains(expected);
}
// 开头检查
public static boolean startsWith(String actual, String prefix) {
return actual != null && actual.startsWith(prefix);
}
// 结尾检查
public static boolean endsWith(String actual, String suffix) {
return actual != null && actual.endsWith(suffix);
}
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.com");
WebElement titleElement = driver.findElement(By.tagName("h1"));
String actualTitle = titleElement.getText();
String expectedTitle = "Welcome to Our Website";
// 各种比较方式
System.out.println("精确匹配: " + exactMatch(actualTitle, expectedTitle));
System.out.println("忽略大小写: " + caseInsensitiveMatch(actualTitle, expectedTitle));
System.out.println("包含'Welcome': " + contains(actualTitle, "Welcome"));
System.out.println("以'Welcome'开头: " + startsWith(actualTitle, "Welcome"));
System.out.println("以'Website'结尾: " + endsWith(actualTitle, "Website"));
} finally {
driver.quit();
}
}
}
3.2 使用断言进行文本验证
import org.junit.Assert;
import org.testng.Assert;
public class TextAssertions {
public void validatePageTitle(WebDriver driver, String expectedTitle) {
String actualTitle = driver.getTitle();
// JUnit断言
Assert.assertEquals("页面标题不匹配", expectedTitle, actualTitle);
// TestNG断言
Assert.assertEquals(actualTitle, expectedTitle, "页面标题不匹配");
}
public void validateElementText(WebElement element, String expectedText) {
String actualText = element.getText().trim();
// 精确匹配
Assert.assertEquals(actualText, expectedText, "元素文本不匹配");
// 包含检查
Assert.assertTrue(actualText.contains(expectedText),
"元素文本不包含期望内容: " + expectedText);
}
public void validateTextNotEmpty(WebElement element) {
String text = element.getText();
Assert.assertNotNull(text, "元素文本为null");
Assert.assertFalse(text.trim().isEmpty(), "元素文本为空");
}
}
4. 字符串格式化和转换
4.1 数字和字符串转换
public class StringConversion {
// 从页面元素中提取数字
public static double extractNumber(String text) {
// 移除非数字字符,保留小数点和负号
String numberStr = text.replaceAll("[^\d.-]", "");
try {
return Double.parseDouble(numberStr);
} catch (NumberFormatException e) {
System.out.println("无法解析数字: " + text);
return 0.0;
}
}
// 格式化价格显示
public static String formatPrice(double price) {
return String.format("¥%.2f", price);
}
// 格式化百分比
public static String formatPercentage(double value) {
return String.format("%.1f%%", value * 100);
}
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://shopping-site.com");
// 获取价格文本并转换
WebElement priceElement = driver.findElement(By.className("price"));
String priceText = priceElement.getText(); // "¥299.99"
double price = extractNumber(priceText);
System.out.println("原始价格文本: " + priceText);
System.out.println("提取的数字: " + price);
System.out.println("格式化价格: " + formatPrice(price));
// 计算折扣
double discount = 0.15;
double discountedPrice = price * (1 - discount);
System.out.println("折扣: " + formatPercentage(discount));
System.out.println("折后价: " + formatPrice(discountedPrice));
} finally {
driver.quit();
}
}
}
4.2 日期和时间字符串处理
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class DateStringHandling {
// 解析日期字符串
public static LocalDate parseDate(String dateStr, String pattern) {
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return LocalDate.parse(dateStr, formatter);
} catch (DateTimeParseException e) {
System.out.println("日期解析失败: " + dateStr);
return null;
}
}
// 格式化日期
public static String formatDate(LocalDate date, String pattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return date.format(formatter);
}
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://booking-site.com");
// 获取日期文本
WebElement dateElement = driver.findElement(By.id("checkInDate"));
String dateText = dateElement.getText(); // "2024-03-15"
// 解析日期
LocalDate date = parseDate(dateText, "yyyy-MM-dd");
if (date != null) {
// 转换为不同格式
String chineseFormat = formatDate(date, "yyyy年MM月dd日");
String usFormat = formatDate(date, "MM/dd/yyyy");
System.out.println("原始日期: " + dateText);
System.out.println("中文格式: " + chineseFormat);
System.out.println("美式格式: " + usFormat);
}
} finally {
driver.quit();
}
}
}
5. 正则表达式在自动化测试中的应用
5.1 常用正则表达式模式
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexOperations {
// 验证邮箱格式
public static boolean isValidEmail(String email) {
String emailRegex = "^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})$";
Pattern pattern = Pattern.compile(emailRegex);
return pattern.matcher(email).matches();
}
// 验证手机号格式(中国)
public static boolean isValidChinesePhone(String phone) {
String phoneRegex = "^1[3-9]\d{9}$";
Pattern pattern = Pattern.compile(phoneRegex);
return pattern.matcher(phone).matches();
}
// 提取URL中的域名
public static String extractDomain(String url) {
String domainRegex = "https?://([^/]+)";
Pattern pattern = Pattern.compile(domainRegex);
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}
// 提取文本中的所有数字
public static String[] extractNumbers(String text) {
String numberRegex = "\d+";
Pattern pattern = Pattern.compile(numberRegex);
Matcher matcher = pattern.matcher(text);
java.util.List<String> numbers = new java.util.ArrayList<>();
while (matcher.find()) {
numbers.add(matcher.group());
}
return numbers.toArray(new String[0]);
}
public static void main(String[] args) {
// 测试正则表达式
String email = "user@example.com";
String phone = "13812345678";
String url = "https://www.example.com/page";
String text = "订单号:12345,金额:299元,数量:3件";
System.out.println("邮箱验证: " + isValidEmail(email));
System.out.println("手机号验证: " + isValidChinesePhone(phone));
System.out.println("提取域名: " + extractDomain(url));
System.out.println("提取数字: " + java.util.Arrays.toString(extractNumbers(text)));
}
}
5.2 在Selenium中使用正则表达式验证
public class RegexValidationInSelenium {
public void validateFormFields(WebDriver driver) {
// 验证邮箱输入框
WebElement emailInput = driver.findElement(By.id("email"));
String emailValue = emailInput.getAttribute("value");
if (!isValidEmail(emailValue)) {
System.out.println("邮箱格式不正确: " + emailValue);
}
// 验证手机号输入框
WebElement phoneInput = driver.findElement(By.id("phone"));
String phoneValue = phoneInput.getAttribute("value");
if (!isValidChinesePhone(phoneValue)) {
System.out.println("手机号格式不正确: " + phoneValue);
}
// 验证页面上的价格格式
WebElement priceElement = driver.findElement(By.className("price"));
String priceText = priceElement.getText();
String priceRegex = "¥\d+\.\d{2}";
if (!priceText.matches(priceRegex)) {
System.out.println("价格格式不正确: " + priceText);
}
}
// 使用正则表达式查找元素
public void findElementsByRegex(WebDriver driver) {
// 查找所有class名包含"btn"的按钮
String xpath = "//button[contains(@class, 'btn')]";
java.util.List<WebElement> buttons = driver.findElements(By.xpath(xpath));
for (WebElement button : buttons) {
String buttonText = button.getText();
System.out.println("找到按钮: " + buttonText);
}
}
}
6. 字符串分割和拼接
6.1 字符串分割操作
public class StringSplitOperations {
// 分割面包屑导航
public static String[] splitBreadcrumb(String breadcrumb) {
// 假设面包屑格式为: "首页 > 产品分类 > 具体产品"
return breadcrumb.split("\s*>\s*");
}
// 分割标签列表
public static String[] splitTags(String tags) {
// 假设标签格式为: "Java, Selenium, 自动化测试, WebDriver"
return tags.split("\s*,\s*");
}
// 解析表格行数据
public static String[] parseTableRow(String rowText) {
// 假设表格数据用制表符或多个空格分隔
return rowText.split("\s{2,}|\t");
}
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.com");
// 处理面包屑导航
WebElement breadcrumbElement = driver.findElement(By.className("breadcrumb"));
String breadcrumbText = breadcrumbElement.getText();
String[] breadcrumbParts = splitBreadcrumb(breadcrumbText);
System.out.println("面包屑导航:");
for (int i = 0; i < breadcrumbParts.length; i++) {
System.out.println(" " + i + ": " + breadcrumbParts[i]);
}
// 处理标签列表
WebElement tagsElement = driver.findElement(By.className("tags"));
String tagsText = tagsElement.getText();
String[] tagArray = splitTags(tagsText);
System.out.println("标签列表:");
for (String tag : tagArray) {
System.out.println(" - " + tag);
}
} finally {
driver.quit();
}
}
}
6.2 字符串拼接操作
public class StringConcatenation {
// 构建搜索查询字符串
public static String buildSearchQuery(String[] keywords) {
return String.join(" AND ", keywords);
}
// 构建文件路径
public static String buildFilePath(String... pathParts) {
return String.join(System.getProperty("file.separator"), pathParts);
}
// 构建URL参数
public static String buildUrlParams(java.util.Map<String, String> params) {
java.util.List<String> paramList = new java.util.ArrayList<>();
for (java.util.Map.Entry<String, String> entry : params.entrySet()) {
paramList.add(entry.getKey() + "=" + entry.getValue());
}
return String.join("&", paramList);
}
// 使用StringBuilder进行高效拼接
public static String buildReport(java.util.List<String> testResults) {
StringBuilder report = new StringBuilder();
report.append("测试报告n");
report.append("=" .repeat(20)).append("n");
for (int i = 0; i < testResults.size(); i++) {
report.append(String.format("%d. %sn", i + 1, testResults.get(i)));
}
return report.toString();
}
public static void main(String[] args) {
// 测试字符串拼接功能
String[] keywords = {"Selenium", "WebDriver", "自动化"};
System.out.println("搜索查询: " + buildSearchQuery(keywords));
String filePath = buildFilePath("C:", "Users", "test", "reports", "result.txt");
System.out.println("文件路径: " + filePath);
java.util.Map<String, String> params = new java.util.HashMap<>();
params.put("page", "1");
params.put("size", "10");
params.put("sort", "name");
System.out.println("URL参数: " + buildUrlParams(params));
java.util.List<String> results = java.util.Arrays.asList(
"登录测试 - 通过",
"搜索测试 - 通过",
"购买测试 - 失败"
);
System.out.println(buildReport(results));
}
}
7. 处理特殊字符和编码
7.1 处理HTML实体和特殊字符
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
public class SpecialCharacterHandling {
// 处理HTML实体
public static String decodeHtmlEntities(String text) {
return text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace(""", """)
.replace("'", "'")
.replace(" ", " ");
}
// URL编码
public static String urlEncode(String text) {
try {
return URLEncoder.encode(text, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
return text;
}
}
// URL解码
public static String urlDecode(String text) {
try {
return URLDecoder.decode(text, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
return text;
}
}
// 移除不可见字符
public static String removeInvisibleChars(String text) {
// 移除零宽字符和其他不可见字符
return text.replaceAll("[\u200B-\u200D\uFEFF]", "")
.replaceAll("[\u0000-\u001F\u007F]", "");
}
// 处理换行符
public static String normalizeLineBreaks(String text) {
return text.replaceAll("\r\n|\r|\n", System.lineSeparator());
}
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.com");
// 获取可能包含HTML实体的文本
WebElement element = driver.findElement(By.className("content"));
String rawText = element.getText();
// 处理特殊字符
String cleanText = decodeHtmlEntities(rawText);
cleanText = removeInvisibleChars(cleanText);
cleanText = normalizeLineBreaks(cleanText);
System.out.println("原始文本: " + rawText);
System.out.println("清理后文本: " + cleanText);
// URL编码示例
String searchTerm = "Java 自动化测试";
String encodedTerm = urlEncode(searchTerm);
System.out.println("编码前: " + searchTerm);
System.out.println("编码后: " + encodedTerm);
System.out.println("解码后: " + urlDecode(encodedTerm));
} finally {
driver.quit();
}
}
}
7.2 处理不同编码格式
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class EncodingHandling {
// 检测字符串编码
public static void detectEncoding(String text) {
byte[] bytes = text.getBytes();
// 尝试不同编码
String[] encodings = {"UTF-8", "GBK", "GB2312", "ISO-8859-1"};
for (String encoding : encodings) {
try {
Charset charset = Charset.forName(encoding);
String decoded = new String(bytes, charset);
System.out.println(encoding + ": " + decoded);
} catch (Exception e) {
System.out.println(encoding + ": 解码失败");
}
}
}
// 转换编码
public static String convertEncoding(String text, String fromEncoding, String toEncoding) {
try {
byte[] bytes = text.getBytes(fromEncoding);
return new String(bytes, toEncoding);
} catch (Exception e) {
System.out.println("编码转换失败: " + e.getMessage());
return text;
}
}
// 确保UTF-8编码
public static String ensureUtf8(String text) {
return new String(text.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
}
}
8. 实际应用场景
8.1 电商网站价格比较
public class PriceComparisonExample {
public static class Product {
private String name;
private double price;
private String currency;
public Product(String name, double price, String currency) {
this.name = name;
this.price = price;
this.currency = currency;
}
// getters and setters
public String getName() { return name; }
public double getPrice() { return price; }
public String getCurrency() { return currency; }
}
// 从价格文本中提取价格信息
public static Product extractProductInfo(WebElement productElement) {
String nameText = productElement.findElement(By.className("product-name")).getText();
String priceText = productElement.findElement(By.className("price")).getText();
// 解析价格 - 支持多种格式: "¥299.99", "$29.99", "€25.50"
String currency = "";
double price = 0.0;
if (priceText.startsWith("¥")) {
currency = "CNY";
price = Double.parseDouble(priceText.substring(1));
} else if (priceText.startsWith("$")) {
currency = "USD";
price = Double.parseDouble(priceText.substring(1));
} else if (priceText.startsWith("€")) {
currency = "EUR";
price = Double.parseDouble(priceText.substring(1));
}
return new Product(nameText.trim(), price, currency);
}
// 比较产品价格
public static void compareProducts(WebDriver driver) {
java.util.List<WebElement> productElements =
driver.findElements(By.className("product-item"));
java.util.List<Product> products = new java.util.ArrayList<>();
for (WebElement element : productElements) {
Product product = extractProductInfo(element);
products.add(product);
}
// 按价格排序
products.sort((p1, p2) -> Double.compare(p1.getPrice(), p2.getPrice()));
System.out.println("产品价格排序(从低到高):");
for (Product product : products) {
System.out.printf("%-30s %s%.2fn",
product.getName(),
getCurrencySymbol(product.getCurrency()),
product.getPrice());
}
}
private static String getCurrencySymbol(String currency) {
switch (currency) {
case "CNY": return "¥";
case "USD": return "$";
case "EUR": return "€";
default: return "";
}
}
}
8.2 表单数据验证
public class FormValidationExample {
// 验证用户注册表单
public static void validateRegistrationForm(WebDriver driver) {
// 获取表单字段
WebElement usernameField = driver.findElement(By.id("username"));
WebElement emailField = driver.findElement(By.id("email"));
WebElement phoneField = driver.findElement(By.id("phone"));
WebElement passwordField = driver.findElement(By.id("password"));
// 获取字段值
String username = usernameField.getAttribute("value");
String email = emailField.getAttribute("value");
String phone = phoneField.getAttribute("value");
String password = passwordField.getAttribute("value");
// 验证用户名
if (!isValidUsername(username)) {
System.out.println("用户名格式不正确: " + username);
}
// 验证邮箱
if (!isValidEmail(email)) {
System.out.println("邮箱格式不正确: " + email);
}
// 验证手机号
if (!isValidChinesePhone(phone)) {
System.out.println("手机号格式不正确: " + phone);
}
// 验证密码强度
if (!isStrongPassword(password)) {
System.out.println("密码强度不够: " + getPasswordStrengthMessage(password));
}
}
// 验证用户名格式
public static boolean isValidUsername(String username) {
// 用户名:3-20个字符,只能包含字母、数字、下划线
String usernameRegex = "^[a-zA-Z0-9_]{3,20}$";
return username != null && username.matches(usernameRegex);
}
// 验证密码强度
public static boolean isStrongPassword(String password) {
if (password == null || password.length() < 8) {
return false;
}
// 至少包含一个大写字母、一个小写字母、一个数字、一个特殊字符
boolean hasUpper = password.matches(".*[A-Z].*");
boolean hasLower = password.matches(".*[a-z].*");
boolean hasDigit = password.matches(".*\d.*");
boolean hasSpecial = password.matches(".*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?].*");
return hasUpper && hasLower && hasDigit && hasSpecial;
}
// 获取密码强度提示信息
public static String getPasswordStrengthMessage(String password) {
java.util.List<String> requirements = new java.util.ArrayList<>();
if (password == null || password.length() < 8) {
requirements.add("至少8个字符");
}
if (!password.matches(".*[A-Z].*")) {
requirements.add("至少一个大写字母");
}
if (!password.matches(".*[a-z].*")) {
requirements.add("至少一个小写字母");
}
if (!password.matches(".*\d.*")) {
requirements.add("至少一个数字");
}
if (!password.matches(".*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?].*")) {
requirements.add("至少一个特殊字符");
}
return "密码需要包含: " + String.join(", ", requirements);
}
}
8.3 日志分析和报告生成
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LogAnalysisExample {
public static class TestResult {
private String testName;
private String status;
private String message;
private LocalDateTime timestamp;
public TestResult(String testName, String status, String message) {
this.testName = testName;
this.status = status;
this.message = message;
this.timestamp = LocalDateTime.now();
}
// getters
public String getTestName() { return testName; }
public String getStatus() { return status; }
public String getMessage() { return message; }
public LocalDateTime getTimestamp() { return timestamp; }
}
// 收集测试结果
public static java.util.List<TestResult> collectTestResults(WebDriver driver) {
java.util.List<TestResult> results = new java.util.ArrayList<>();
// 假设页面上有测试结果表格
java.util.List<WebElement> resultRows =
driver.findElements(By.cssSelector(".test-results tbody tr"));
for (WebElement row : resultRows) {
java.util.List<WebElement> cells = row.findElements(By.tagName("td"));
if (cells.size() >= 3) {
String testName = cells.get(0).getText().trim();
String status = cells.get(1).getText().trim();
String message = cells.get(2).getText().trim();
results.add(new TestResult(testName, status, message));
}
}
return results;
}
// 生成测试报告
public static String generateTestReport(java.util.List<TestResult> results) {
StringBuilder report = new StringBuilder();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 报告头部
report.append("自动化测试报告n");
report.append("=".repeat(50)).append("n");
report.append("生成时间: ").append(LocalDateTime.now().format(formatter)).append("nn");
// 统计信息
long passedCount = results.stream().filter(r -> "PASS".equals(r.getStatus())).count();
long failedCount = results.stream().filter(r -> "FAIL".equals(r.getStatus())).count();
long skippedCount = results.stream().filter(r -> "SKIP".equals(r.getStatus())).count();
report.append("测试统计:n");
report.append(String.format(" 总计: %dn", results.size()));
report.append(String.format(" 通过: %dn", passedCount));
report.append(String.format(" 失败: %dn", failedCount));
report.append(String.format(" 跳过: %dn", skippedCount));
report.append(String.format(" 成功率: %.1f%%nn",
(double) passedCount / results.size() * 100));
// 详细结果
report.append("详细结果:n");
report.append("-".repeat(50)).append("n");
for (TestResult result : results) {
report.append(String.format("%-30s [%s] %sn",
result.getTestName(),
result.getStatus(),
result.getMessage()));
}
// 失败测试汇总
java.util.List<TestResult> failedTests = results.stream()
.filter(r -> "FAIL".equals(r.getStatus()))
.collect(java.util.stream.Collectors.toList());
if (!failedTests.isEmpty()) {
report.append("n失败测试详情:n");
report.append("-".repeat(50)).append("n");
for (TestResult failed : failedTests) {
report.append(String.format("测试名称: %sn", failed.getTestName()));
report.append(String.format("失败原因: %sn", failed.getMessage()));
report.append(String.format("时间: %snn",
failed.getTimestamp().format(formatter)));
}
}
return report.toString();
}
}
9. 最佳实践和注意事项
9.1 性能优化
public class StringPerformanceOptimization {
// 使用StringBuilder进行大量字符串拼接
public static String efficientStringBuilding(java.util.List<String> items) {
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item).append("n");
}
return sb.toString();
}
// 避免在循环中进行字符串拼接
public static String inefficientWay(java.util.List<String> items) {
String result = "";
for (String item : items) {
result += item + "n"; // 每次都创建新的String对象
}
return result;
}
// 使用String.format进行格式化
public static String formatMessage(String name, int count, double price) {
// 推荐方式
return String.format("用户 %s 购买了 %d 件商品,总价 %.2f 元", name, count, price);
// 不推荐的拼接方式
// return "用户 " + name + " 购买了 " + count + " 件商品,总价 " + price + " 元";
}
// 缓存编译的正则表达式
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})$");
public static boolean isValidEmailCached(String email) {
return EMAIL_PATTERN.matcher(email).matches();
}
}
9.2 错误处理和异常管理
public class StringErrorHandling {
// 安全的字符串操作
public static String safeSubstring(String str, int start, int end) {
if (str == null) {
return "";
}
int length = str.length();
start = Math.max(0, Math.min(start, length));
end = Math.max(start, Math.min(end, length));
return str.substring(start, end);
}
// 安全的数字转换
public static double safeParseDouble(String str, double defaultValue) {
if (str == null || str.trim().isEmpty()) {
return defaultValue;
}
try {
return Double.parseDouble(str.trim());
} catch (NumberFormatException e) {
System.out.println("数字解析失败: " + str + ", 使用默认值: " + defaultValue);
return defaultValue;
}
}
// 安全的元素文本获取
public static String safeGetText(WebElement element) {
try {
return element != null ? element.getText().trim() : "";
} catch (Exception e) {
System.out.println("获取元素文本失败: " + e.getMessage());
return "";
}
}
// 带重试的文本获取
public static String getTextWithRetry(WebElement element, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
try {
String text = element.getText();
if (text != null && !text.trim().isEmpty()) {
return text.trim();
}
} catch (Exception e) {
System.out.println("第 " + (i + 1) + " 次获取文本失败: " + e.getMessage());
if (i < maxRetries - 1) {
try {
Thread.sleep(1000); // 等待1秒后重试
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
}
return "";
}
}
9.3 代码组织和可维护性
// 字符串常量管理
public class StringConstants {
// 正则表达式常量
public static final String EMAIL_REGEX = "^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})$";
public static final String PHONE_REGEX = "^1[3-9]\d{9}$";
public static final String PASSWORD_REGEX = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$";
// 错误消息常量
public static final String INVALID_EMAIL_MSG = "邮箱格式不正确";
public static final String INVALID_PHONE_MSG = "手机号格式不正确";
public static final String WEAK_PASSWORD_MSG = "密码强度不够";
// 格式化模板
public static final String PRICE_FORMAT = "¥%.2f";
public static final String PERCENTAGE_FORMAT = "%.1f%%";
public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
}
// 字符串工具类
public class StringUtils {
// 私有构造函数防止实例化
private StringUtils() {}
// 检查字符串是否为空
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
// 检查字符串是否不为空
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
// 安全的字符串比较
public static boolean equals(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
// 忽略大小写的安全比较
public static boolean equalsIgnoreCase(String str1, String str2) {
return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
}
// 截断字符串
public static String truncate(String str, int maxLength) {
if (isEmpty(str) || str.length() <= maxLength) {
return str;
}
return str.substring(0, maxLength) + "...";
}
// 首字母大写
public static String capitalize(String str) {
if (isEmpty(str)) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
}
9.4 测试和调试技巧
public class StringDebuggingTips {
// 调试字符串内容
public static void debugString(String str, String label) {
System.out.println("=== " + label + " ===");
System.out.println("原始: '" + str + "'");
System.out.println("长度: " + (str != null ? str.length() : "null"));
if (str != null) {
System.out.println("去空格: '" + str.trim() + "'");
System.out.println("去空格长度: " + str.trim().length());
// 显示不可见字符
StringBuilder visible = new StringBuilder();
for (char c : str.toCharArray()) {
if (Character.isISOControl(c)) {
visible.append("\u").append(String.format("%04X", (int) c));
} else {
visible.append(c);
}
}
System.out.println("可见字符: '" + visible.toString() + "'");
}
System.out.println("================n");
}
// 比较两个字符串的差异
public static void compareStrings(String str1, String str2) {
System.out.println("字符串比较:");
System.out.println("字符串1: '" + str1 + "'");
System.out.println("字符串2: '" + str2 + "'");
System.out.println("相等: " + Objects.equals(str1, str2));
System.out.println("忽略大小写相等: " +
(str1 != null && str2 != null && str1.equalsIgnoreCase(str2)));
if (str1 != null && str2 != null) {
System.out.println("长度1: " + str1.length());
System.out.println("长度2: " + str2.length());
// 找出第一个不同的字符位置
int minLength = Math.min(str1.length(), str2.length());
for (int i = 0; i < minLength; i++) {
if (str1.charAt(i) != str2.charAt(i)) {
System.out.println("第一个差异位置: " + i);
System.out.println("字符1: '" + str1.charAt(i) + "' (Unicode: " +
(int) str1.charAt(i) + ")");
System.out.println("字符2: '" + str2.charAt(i) + "' (Unicode: " +
(int) str2.charAt(i) + ")");
break;
}
}
}
System.out.println();
}
// 记录字符串操作日志
public static String logStringOperation(String operation, String input, String output) {
String logMessage = String.format("[%s] %s: '%s' -> '%s'",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
operation, input, output);
System.out.println(logMessage);
return output;
}
}
总结
字符串操作在Java+Selenium自动化测试中扮演着重要角色。掌握这些技能可以帮助我们:
- 高效处理页面数据:从网页元素中提取、验证和转换文本信息
- 提高测试可靠性:通过正确的字符串比较和验证确保测试准确性
- 优化代码性能:使用合适的字符串操作方法避免性能问题
- 增强代码可维护性:通过工具类和常量管理提高代码质量
关键要点:
- 始终考虑null值和空字符串的处理
- 使用StringBuilder进行大量字符串拼接
- 缓存编译的正则表达式以提高性能
- 合理使用字符串工具类和常量管理
- 在调试时充分利用字符串分析工具
通过掌握这些字符串操作技巧,您可以编写更加健壮和高效的自动化测试代码。