
1. 项目概述为什么字符串操作是自动化测试的基石如果你刚开始用 Python 和 Playwright 做自动化测试可能会觉得定位元素、点击按钮、填写表单是核心。但当你真正开始写脚本尤其是处理动态数据、验证页面内容或者生成测试报告时你会发现字符串操作无处不在它才是连接脚本逻辑与真实世界的“粘合剂”。想象一下你需要从页面上抓取一个订单号它可能是“订单OD-20240521-00123”这样的格式你如何只提取出“OD-20240521-00123”或者你需要构造一个包含当前日期的搜索关键词又或者需要验证一个提示信息是否包含了预期的错误码。这些场景都离不开对字符串的“精雕细琢”。我见过不少新手写的脚本因为字符串处理不当导致测试用例异常脆弱。比如用简单的in判断包含关系结果因为一个多余的空格导致断言失败或者拼接文件路径时因为操作系统不同Windows用\Mac/Linux用/而脚本无法跨平台运行。这些问题看似微小积累起来却会让你的自动化测试项目维护成本急剧上升。因此掌握扎实的字符串操作技能不是“锦上添花”而是“雪中送炭”它能让你写的脚本更健壮、更灵活、更专业。本篇作为“字符串操作”的下篇我们将深入那些在自动化测试实战中高频使用却又容易被忽略的高级技巧和最佳实践。2. 核心需求解析自动化测试中的字符串应用场景在自动化测试脚本里字符串操作绝不是孤立的知识点它紧密服务于具体的测试场景。理解这些场景你才能明白为什么需要学习特定的方法而不是死记硬背 API。2.1 场景一动态文本的提取与验证这是最常见的场景。页面上的文本往往是动态生成的比如商品价格“129.00”、用户欢迎语“你好张三”、或状态提示“提交成功流水号TX998877”。你的测试脚本需要精确提取从一大段文本中抠出你关心的那部分如流水号。模糊匹配验证提示信息是否包含了关键词语而不必完全一致。格式校验验证提取出的字符串是否符合预期的格式如邮箱格式、手机号格式。2.2 场景二测试数据的构造与拼接测试数据很少是硬编码的。为了提高测试的覆盖率和可靠性我们经常需要动态构造数据。参数化数据将测试用例与数据分离用字符串模板来生成不同的测试输入。例如搜索关键词 f测试产品_{random.randint(100,999)}。文件与路径操作拼接日志文件路径、截图保存路径、测试数据文件路径。os.path.join()是核心但理解字符串拼接是基础。API请求构建组装 RESTful API 的 URL 和请求体JSON 字符串。2.3 场景三清理与标准化从网页或外部系统获取的数据常常包含“噪音”。去除空白符去除用户输入或页面文本首尾多余的空格、换行符、制表符这是避免断言失败的必备步骤。统一格式例如将金额数字中的逗号分隔符去除1,299.00 - 1299.00以便进行数值比较。敏感信息脱敏在日志或报告中将手机号、邮箱等部分字符替换为星号。2.4 场景四复杂断言与报告生成断言Assert是测试的灵魂而字符串断言占了很大比重。正则表达式断言当需要验证一个复杂模式时如“以‘ERR_’开头的错误码”正则表达式是最强大的工具。报告内容生成将测试结果、耗时、错误信息等组织成人类可读的字符串写入报告或控制台。理解了这些场景我们再去看具体的字符串方法就会有的放矢。下面我们就进入实战环节拆解这些场景下的核心操作。3. 核心细节解析与实操要点Python 提供了丰富的字符串处理方法在自动化测试中我们主要聚焦于那些能直接提升脚本稳定性和效率的功能。上篇可能已经覆盖了split(),join(),strip()等基础方法下篇我们重点攻克更高级和更“实战”的部分。3.1 字符串格式化三种主流方式的抉择为什么不用简单的号拼接因为在构造包含变量、路径或动态数据的字符串时号拼接不仅繁琐易错而且性能不佳。Python 提供了三种更优的方案。1.str.format()方法清晰与灵活的代表这是目前非常推荐的方式平衡了可读性和功能。# 基础用法通过位置参数 order_info 订单号{} 金额{}元.format(OD123456, 299.9) print(order_info) # 输出订单号OD123456 金额299.9元 # 通过关键字参数更清晰 log_message 用户 {username} 在 {time} 执行了 {action}.format( usernametester, time2024-05-21 10:00, action登录 ) print(log_message) # 格式控制数字位数、小数位 price 129.5 formatted_price 价格{:10.2f}.format(price) # 右对齐宽度10保留2位小数 print(formatted_price) # 输出价格 129.50实操心得在构造复杂的日志信息或报告条目时我强烈推荐使用关键字参数的format()。这样即使以后要调整变量的顺序或增加新变量代码也更容易维护不会因为参数位置变动而出错。2. f-string (Python 3.6)简洁与高效的王者这是目前最流行、写起来最舒服的方式直接在字符串内嵌入表达式。user “张三” item_count 5 total 299.99 # 直接嵌入变量 message f”用户 {user} 购买了 {item_count} 件商品总计 {total} 元。” print(message) # 嵌入表达式和函数调用 import datetime filename f”screenshot_{datetime.datetime.now().strftime(‘%Y%m%d_%H%M%S’)}.png” print(filename) # 例如screenshot_20240521_143025.png # 格式控制同样强大 formatted f”总价{total:10.2f}” print(formatted)注意事项f-string 虽然好用但在一些极老的 Python 3.5 或 2.7 环境虽然现在很少见中无法使用。如果你的项目有严格的兼容性要求需要确认环境版本。但在绝大多数新项目中f-string 是首选。3. 百分号%格式化遗留代码中的常客你可能会在旧脚本或某些文档中看到它了解即可新代码不建议使用。name “李四” age 30 info “姓名%s 年龄%d” % (name, age)如何选择对于新的自动化测试项目优先使用f-string其次是str.format()。%格式化只用于维护旧代码。3.2 正则表达式Regex处理非结构化文本的瑞士军刀当简单的find()、split()无能为力时正则表达式就是你的终极武器。它用于匹配、查找、替换符合复杂规则的字符串。Playwright 自动化测试中常用于验证动态文本、提取特定模式的数据。核心概念速览\d匹配任意数字等价于[0-9]。\w匹配字母、数字、下划线。.匹配任意单个字符除了换行符。*匹配前面的子表达式零次或多次。匹配一次或多次。?匹配零次或一次。{n,m}匹配至少 n 次至多 m 次。[]字符集合匹配其中任意一个字符。()分组提取匹配到的内容。Pythonre模块实战import re # 场景从一段文本中提取所有手机号 text “”” 请联系客服13800138000 或 13912345678。 备用电话15098765432无效号码12345。 “”” # 定义手机号正则简单版国内11位 phone_pattern r’1[3-9]\d{9}’ # r 表示原始字符串避免转义麻烦 # 方法1: findall 查找所有匹配 phone_numbers re.findall(phone_pattern, text) print(f”找到的手机号{phone_numbers}”) # 输出[‘13800138000’ ‘13912345678’ ‘15098765432’] # 方法2: search 查找第一个匹配 first_match re.search(phone_pattern, text) if first_match: print(f”第一个手机号{first_match.group()}”) # 输出13800138000 print(f”匹配位置{first_match.start()} - {first_match.end()}”) # 场景验证字符串是否符合特定格式例如订单号格式 OD-YYYYMMDD-XXXXX def is_valid_order_id(order_id): pattern r’^OD-\d{8}-\d{5}$’ # ^ 开头$ 结尾确保完全匹配 return re.match(pattern, order_id) is not None print(is_valid_order_id(“OD-20240521-00123”)) # True print(is_valid_order_id(“OD-20240521-123”)) # False末尾数字不是5位 print(is_valid_order_id(“前缀OD-20240521-00123”)) # False开头不匹配 # 场景替换敏感信息 log_content “用户手机号 13800138000 登录成功。” anonymized_log re.sub(r’1[3-9]\d{9}’, ‘***’ log_content) print(anonymized_log) # 输出用户手机号 *** 登录成功。避坑指南正则表达式功能强大但容易写错。建议先用在线的正则表达式测试工具如 regex101.com调试你的模式确认无误后再写入代码。对于复杂的正则加上详细的注释说明每一部分匹配什么。谨慎使用贪婪匹配*和默认是贪婪的有时需要使用非贪婪匹配*?或?。例如提取 HTML 标签内容时贪婪匹配可能会匹配到最后一个标签而非贪婪匹配则匹配最近的一个。3.3 字符串的编码与解码绕不开的“乱码”问题在自动化测试中当你处理文件 I/O、网络请求如 Playwright 的 API 响应或者遇到中文环境时可能会碰到编码问题。核心原则在内存中Python 字符串是 Unicodestr类型存储或传输时需要编码为字节串bytes类型。编码Encodestr-bytes。将人类可读的字符串转换为计算机存储/传输的字节序列。解码Decodebytes-str。将字节序列转换回人类可读的字符串。# 常见的编码方式UTF-8 (推荐), GBK (中文Windows系统旧文件) text “自动化测试” # 编码为 UTF-8 字节串 bytes_utf8 text.encode(‘utf-8’) print(bytes_utf8) # 输出b’\xe8\x87\xaa\xe5\x8a\xa8\xe5\x8c\x96\xe6\xb5\x8b\xe8\xaf\x95’ # 解码回字符串 decoded_text bytes_utf8.decode(‘utf-8’) print(decoded_text) # 输出自动化测试 # 如果解码时用了错误的编码就会产生乱码 try: wrong_decoded bytes_utf8.decode(‘gbk’) print(wrong_decoded) # 可能会输出乱码如 “鑷姩鍖栨祴璇?” except UnicodeDecodeError as e: print(f”解码错误{e}”)实操心得在 Playwright 自动化测试中我建议始终明确指定编码。读写文件时with open(‘log.txt’ ‘w’ encoding‘utf-8’) as f:如果从某个旧系统页面抓取的文本出现乱码可以尝试常见的编码如‘gbk’‘gb2312’‘iso-8859-1’进行解码。Playwright 自身返回的文本内容通常是 UTF-8一般无需担心。4. 实操过程与核心环节实现现在让我们把这些字符串技巧融入一个完整的 Playwright 自动化测试场景中。假设我们要测试一个电商网站的订单搜索功能输入订单号验证结果列表是否正确显示订单信息并将测试结果写入报告。4.1 环境准备与场景设定首先确保你的环境已就绪。# 安装必要的库 pip install playwright playwright install chromium # 安装浏览器我们的测试页面假设有一个搜索框#search-input和一个搜索按钮#search-btn搜索结果会显示在一个表格table#order-list中每一行包含订单号、金额和状态。4.2 测试脚本编写字符串操作贯穿始终import re import datetime from playwright.sync_api import sync_playwright import csv def test_order_search(): “””测试订单搜索功能并演示字符串操作的综合应用。””” with sync_playwright() as p: # 启动浏览器 browser p.chromium.launch(headlessFalse) # 调试时可设为 False context browser.new_context() page context.new_page() # 1. 导航到测试页面 page.goto(“https://your-test-site.com/orders”) # 2. 构造动态测试数据 - 使用 f-string # 假设订单号格式为 “SO-日期-序号” today datetime.datetime.now().strftime(‘%Y%m%d’) test_order_id f”SO-{today}-0001” print(f”构造的测试订单号{test_order_id}”) # 3. 在搜索框输入订单号 search_input page.locator(“#search-input”) search_input.fill(test_order_id) page.locator(“#search-btn”).click() # 等待结果加载 page.wait_for_selector(“table#order-list tr:has-text(‘SO-‘)”) # 4. 提取并验证结果 - 综合运用字符串方法 # 定位结果表格的第一行 first_row page.locator(“table#order-list tbody tr”).first if first_row.count() 0: print(“错误未找到任何订单结果”) browser.close() return False # 获取该行的文本内容 row_text first_row.text_content().strip() # .strip() 去除首尾空白 print(f”获取到的行文本{row_text}”) # 假设行文本格式为“SO-20240521-0001 299.99 已完成” # 5. 使用正则表达式提取关键字段 # 匹配订单号、金额、状态 pattern r’(SO-\d{8}-\d{4})\s([\d.])\s(\S)’ match re.search(pattern row_text) if not match: print(“错误无法从结果中解析出订单信息”) browser.close() return False extracted_order_id extracted_amount extracted_status match.groups() print(f”解析结果 - 订单号{extracted_order_id} 金额{extracted_amount} 状态{extracted_status}”) # 6. 数据清洗与标准化 # 去除金额中的逗号如果有并转换为浮点数进行比较 cleaned_amount extracted_amount.replace(‘’ ‘’) amount_value float(cleaned_amount) expected_amount 299.99 # 7. 执行断言字符串与数值比较 assert extracted_order_id test_order_id f”订单号不匹配预期{test_order_id} 实际{extracted_order_id}” assert abs(amount_value - expected_amount) 0.01 f”金额不匹配预期{expected_amount} 实际{amount_value}” assert extracted_status “已完成” f”状态不匹配预期已完成 实际{extracted_status}” print(“所有断言通过”) # 8. 生成测试报告条目 - 使用 str.format() 组织复杂信息 test_time datetime.datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’) report_entry “测试时间{time} | 用例订单搜索 | 订单号{order_id} | 状态{status} | 金额验证{amount_check}”.format( timetest_time order_idtest_order_id status“PASS” if extracted_status “已完成” else “FAIL” amount_check“OK” if abs(amount_value - expected_amount) 0.01 else “Mismatch” ) print(“测试报告条目”) print(report_entry) # 9. 可选将结果写入CSV文件 - 涉及路径拼接和文件编码 import os report_dir “test_reports” os.makedirs(report_dir exist_okTrue) # 确保目录存在 report_file os.path.join(report_dir f”order_test_{today}.csv”) # 安全的路径拼接 # 检查文件是否存在决定是否写入表头 file_exists os.path.isfile(report_file) with open(report_file ‘a’ newline‘’ encoding‘utf-8-sig’) as f: # utf-8-sig 兼容Excel中文 writer csv.writer(f) if not file_exists: writer.writerow([“测试时间” “订单号” “状态” “金额验证” “详情”]) writer.writerow([test_time test_order_id “PASS” “OK” row_text]) browser.close() return True if __name__ “__main__”: success test_order_search() print(f”测试执行结果{‘成功’ if success else ‘失败’}”)4.3 代码逐段解析与技巧动态数据构造第2步使用f-string结合datetime模块生成当天日期的订单号使得每次测试数据都是新的避免了因数据重复导致的测试干扰。文本提取与清理第4、5、6步text_content().strip()Playwright 获取元素全部文本并立即去除首尾空白这是好习惯。正则表达式r’(SO-\d{8}-\d{4})\s([\d.])\s(\S)’(SO-\d{8}-\d{4})第一个分组匹配订单号。\s匹配一个或多个空白字符空格、制表符等。([\d.])匹配中文货币符号后跟着的数字、逗号和小数点金额可能包含千位分隔符。\s(\S)匹配空白字符后的所有非空白字符作为状态。replace(‘’ ‘’)清洗数据移除金额中的千位分隔符为数值比较做准备。断言第7步断言是测试的检查点。这里展示了字符串相等断言和浮点数近似相等断言因为浮点数计算可能有精度问题。报告生成第8步使用str.format()清晰地组织多个变量生成格式统一的报告条目。关键字参数使得代码更易读。文件操作第9步os.path.join()这是跨平台路径拼接的黄金标准永远比用字符串拼接report_dir “/” filename更安全。encoding‘utf-8-sig’写入 CSV 时使用带 BOM 的 UTF-8可以确保用 Microsoft Excel 打开时中文不会乱码。newline‘’在写入 CSV 时指定可以避免在 Windows 系统上产生空行。这个完整的流程展示了从数据准备、页面交互、信息提取、数据清洗、断言验证到结果记录的全过程中字符串操作是如何作为基础支撑贯穿始终的。5. 常见问题与排查技巧实录即使掌握了方法在实际编码中还是会遇到各种“坑”。下面是我在多年自动化测试中总结的关于字符串操作的常见问题及解决方案。5.1 问题一断言失败因为多了个看不见的换行符或空格现象assert page.text_content(“#msg”) “操作成功”明明页面上显示的就是“操作成功”但断言总是失败。排查text page.text_content(“#msg”) print(repr(text)) # 使用 repr() 函数打印原始表示会显示转义字符 # 输出可能是’操作成功\n’ 或者 ‘ 操作成功 ‘原因text_content()获取的文本可能包含了尾随的换行符\n或空格。解决# 方案1使用 .strip() 去除首尾空白字符 assert page.text_content(“#msg”).strip() “操作成功” # 方案2使用 in 进行包含性断言如果允许 assert “操作成功” in page.text_content(“#msg”) # 方案3使用正则表达式进行模糊匹配 import re assert re.search(r’^操作成功\s*$’ page.text_content(“#msg”)) is not None心得在处理从页面抓取的文本进行断言时第一反应就应该是加上.strip()。这是一个成本极低但能避免大量诡异失败的好习惯。5.2 问题二路径拼接在 Windows 和 Mac/Linux 上表现不一致现象脚本在 Mac 上运行正常在 Windows 上却报错“No such file or directory”。错误代码file_path “reports/” “screenshot_” order_id “.png”原因Windows 使用反斜杠\作为路径分隔符而 Mac/Linux 使用正斜杠/。硬编码的字符串拼接不可移植。解决永远使用os.path.join()。import os report_dir “test_results” filename f”screenshot_{order_id}.png” # 正确做法 file_path os.path.join(report_dir filename) # 在所有操作系统上os.path.join 都会生成正确的路径分隔符如果是从配置文件中读取路径也要注意统一风格或者使用os.path.normpath()进行规范化。5.3 问题三正则表达式匹配不到预期内容或者匹配过多现象写好的正则表达式re.findall(r’\d’ text)要么返回空列表要么把不该匹配的数字也匹配进来了。排查思路检查原始文本用print(repr(text))确认你处理的字符串到底长什么样是否有隐藏字符。使用在线工具调试将你的文本和正则表达式粘贴到 regex101.com 这类网站它能高亮显示匹配部分并解释每个元字符的含义是调试神器。注意贪婪匹配html_snippet ‘div标题/divdiv内容/div’ # 贪婪匹配默认匹配从第一个 div 到最后一个 /div greedy_match re.search(r’div.*/div’ html_snippet) print(greedy_match.group()) # 输出div标题/divdiv内容/div # 非贪婪匹配匹配第一个遇到的 /div non_greedy_match re.search(r’div.*?/div’ html_snippet) print(non_greedy_match.group()) # 输出div标题/div技巧当你发现匹配到的内容比你预期的要多很多时尝试在*或后面加上?改为非贪婪模式。5.4 问题四处理中文时遇到的编码错误UnicodeEncodeError/UnicodeDecodeError现象脚本在打印日志、写入文件或与某些系统交互时遇到‘gbk’ codec can’t encode character …或类似的错误。根本原因编码不一致。内存中的字符串Unicode在输出到控制台或文件时需要转换为特定的字节编码。如果目标环境如 Windows 中文版控制台默认编码如 GBK无法表示字符串中的某些字符如特殊符号或某些生僻字就会报错。通用解决方案明确指定编码在所有文件操作中强制使用 UTF-8。with open(‘log.txt’ ‘w’ encoding‘utf-8’) as f: f.write(包含中文的内容)处理控制台输出如果 Python 脚本输出到控制台乱码可以尝试设置环境变量PYTHONIOENCODINGutf-8或者在代码中重定向标准输出。Playwright 特定Playwright 本身处理文本通常没问题。但如果从某个特定编码的网页抓取文本可以尝试用不同的编码去解码响应内容虽然 Playwright API 通常已处理好。5.5 问题五字符串格式化时变量类型不匹配导致错误现象使用 f-string 或format()时报错TypeError: must be real number not str。错误示例f”价格{price:.2f}”但price变量是一个字符串“129.9”。解决在格式化前确保类型正确。price_from_page page.locator(“#price”).text_content().strip() # 可能是 “129.90” # 需要先清理并转换 price_str price_from_page.replace(‘’ ‘’).replace(‘’ ‘’) try: price_float float(price_str) formatted f”价格{price_float:.2f}” except ValueError: print(f”无法将 ‘{price_from_page}’ 转换为浮点数”) # 或者使用更健壮的正则表达式提取纯数字 import re match re.search(r’[\d.]’ price_from_page) if match: price_float float(match.group().replace(‘’ ‘’))核心原则不要相信任何来自外部页面、文件、API的数据。在用于计算、比较或格式化之前一定要进行清洗、验证和类型转换。字符串操作是 Python 编程的基石在自动化测试领域更是如此。它贯穿了数据准备、交互、验证和报告的全流程。掌握本文介绍的这些核心技巧——特别是格式化、正则表达式和编码处理——能让你写出更健壮、更易维护的测试脚本。记住多使用.strip() 坚持用os.path.join() 善用正则表达式在线调试工具并在文件操作中明确指定encoding‘utf-8’ 就能避开 80% 的字符串相关陷阱。剩下的就是在不断的实战中积累经验形成自己的字符串处理工具箱了。