SeleniumBase与Cypress Real World App自动化测试实践
1. 项目概述SeleniumBase与Cypress Real World App的自动化测试实践最近在尝试使用SeleniumBase框架对Cypress Real World App这个开源项目进行自动化测试时遇到了几个典型的前后端交互问题。作为一款模拟真实金融场景的测试应用Cypress Real World App包含了用户注册、登录、转账等完整业务流程是学习自动化测试的优秀素材。而SeleniumBase作为基于Python的测试框架它整合了Selenium WebDriver和pytest的优势特别适合需要复杂交互的Web应用测试。在测试过程中我主要遇到了两个核心问题注册后不自动跳转登录页面的前端路由问题以及测试用例间登录状态残留导致的流程失败。这两个问题看似简单但涉及到了前后端联调、HTTP代理配置、浏览器存储机制等多个技术点对测试脚本的稳定性影响很大。下面我将详细记录问题排查过程和解决方案这些经验对于任何使用Selenium或类似工具进行Web自动化测试的开发者都值得参考。2. 环境准备与项目配置2.1 测试环境搭建要复现这个测试场景需要准备以下环境Python 3.8环境推荐使用virtualenv创建隔离环境Node.js 16用于运行Cypress Real World App前端Java 8部分后端服务依赖最新版Chrome浏览器安装核心测试依赖pip install seleniumbase pytest克隆Cypress Real World App仓库并启动git clone https://github.com/cypress-io/cypress-realworld-app.git cd cypress-realworld-app npm install npm start2.2 SeleniumBase测试脚本结构典型的测试文件结构如下tests/ ├── conftest.py # pytest配置和fixture ├── test_login.py # 登录相关测试用例 └── pages/ # Page Object模式目录 ├── base.py ├── login.py └── register.py基础测试用例示例from seleniumbase import BaseCase class LoginTest(BaseCase): def test_user_registration(self): self.open(http://localhost:3000) self.type(input[nameusername], testuser) self.type(input[namepassword], Pass1234) self.click(button[typesubmit]) self.assert_text(Welcome, h1)3. 注册跳转问题深度解析3.1 问题现象与初步排查当运行注册流程测试时发现提交注册表单后页面没有自动跳转到登录页而是停留在注册页且没有任何错误提示。通过Chrome开发者工具检查网络请求发现POST /users请求返回了200状态码但前端没有处理成功响应。常见的排查方向CORS跨域问题检查响应头是否包含Access-Control-Allow-Origin后端服务未启动确认后端API是否正常运行前端路由配置检查注册成功后的重定向逻辑3.2 根本原因定位经过抓包分析和手动测试验证发现问题出在前端代码的API请求地址配置上。在authMachine.ts文件中存在硬编码的后端地址const resp await httpClient.post( http://localhost:${backendPort}/users, payload );这里有两个关键问题backendPort变量没有正确从环境变量读取导致实际请求发送到了前端开发服务器(3000端口)使用了绝对路径而非相对路径使得代理配置无法生效3.3 解决方案实现3.3.1 代码修改方案最优解是修改authMachine.ts使用相对路径并确保代理配置正确// 修改后的API调用 const resp await httpClient.post(/users, payload);同时需要完善vite.config.ts中的代理配置避免硬编码端口proxy: { ^/(login|users|checkAuth|graphql|contacts|bankAccounts|transactions|likes|comments|notifications|bankTransfers|testData): { target: http://localhost:${env.VITE_BACKEND_PORT || 3001}, changeOrigin: true, rewrite: (path) path, } }3.3.2 测试脚本适配在SeleniumBase测试中我们需要增加对页面跳转的显式等待和断言def test_user_registration(self): self.open(http://localhost:3000/register) # 填写注册表单 self.type(input[namefirstName], Test) self.type(input[namelastName], User) self.type(input[nameusername], testuser) self.type(input[namepassword], Pass1234) self.click(button[typesubmit]) # 显式等待跳转 self.wait_for_url_contains(/login, timeout10) self.assert_text(Sign in, h1)重要提示在实际测试中应该使用环境变量管理后端地址而不是硬编码在测试脚本中。可以通过pytest的fixture或conftest.py来集中管理测试配置。4. 登录状态残留问题解决方案4.1 问题现象分析当连续运行多个测试用例时发现第二个测试用例开始时应用已经处于登录状态。这是因为浏览器保留了前一个测试的cookies、localStorage和sessionStorage。具体表现为直接访问首页会被重定向到dashboard尝试注册新用户会提示已登录用户相关操作会使用前一个测试的用户凭证4.2 浏览器存储机制解析现代Web应用通常使用三种方式保持登录状态Cookies最常见的方式可设置过期时间localStorage持久化存储除非手动清除sessionStorage标签页级别的临时存储在Cypress Real World App中检查发现主要使用了JWT token存储在localStorage用户信息存储在Redux store部分配置信息使用sessionStorage4.3 测试隔离方案4.3.1 方案一每次测试后清理存储def tearDown(self): # 清除所有cookies self.driver.delete_all_cookies() # 清除localStorage和sessionStorage self.execute_script(window.localStorage.clear();) self.execute_script(window.sessionStorage.clear();) super().tearDown()4.3.2 方案二使用无痕模式修改测试启动配置class LoginTest(BaseCase): classmethod def setUpClass(cls): super().setUpClass() cls.driver cls.get_driver(browserchrome, incognitoTrue)4.3.3 方案三完全独立的测试会话最可靠的方式是为每个测试启动全新的浏览器实例# conftest.py pytest.fixture(scopefunction) def browser(request): driver webdriver.Chrome(optionschrome_options) yield driver driver.quit() # 测试用例 def test_example(browser): sb BaseCase() sb.driver browser sb.open(http://localhost:3000)4.4 最佳实践建议测试顺序设计将需要未登录状态的测试(如注册)放在前面已登录状态的测试放在后面显式登出在测试结束时主动调用登出API使用独立测试账户为每个测试生成唯一的用户名避免冲突并行测试隔离当使用pytest-xdist并行运行时确保每个worker有独立的环境配置5. SeleniumBase高级测试技巧5.1 元素定位策略优化在测试金融类应用时推荐使用以下定位策略# 好的实践 self.type(cssinput[nameamount], 100) # 明确指定选择器类型 self.click(button:contains(Send Payment)) # 文本内容定位 # 避免的做法 self.type(//*[idapp]/div/div[2]/form/div[3]/input, 100) # 脆弱的XPath5.2 等待机制最佳实践金融应用常有异步操作需要合理使用等待# 显式等待转账完成 self.wait_for_element_visible(div.transaction-complete, timeout15) # 自定义等待条件 def balance_updated(self, amount): self.wait_for_text(f${amount}, span.account-balance) # 轮询API结果 def wait_for_transaction(self, tx_id): for _ in range(10): status get_transaction_status(tx_id) if status completed: return time.sleep(1) raise TimeoutError(Transaction not completed)5.3 测试数据管理对于转账等操作建议使用工厂模式创建测试数据# test_data.py class TestDataFactory: staticmethod def create_user(roleuser): return { username: ftest_{uuid.uuid4().hex[:8]}, password: Secure123!, email: ftest_{uuid.uuid4().hex[:8]}example.com } # 测试用例中使用 user TestDataFactory.create_user() self.create_account_via_api(user) # 通过API快速创建测试账户6. 常见问题排查指南6.1 元素定位失败现象测试报告Element not found错误排查步骤使用Chrome开发者工具验证选择器检查iframe嵌套情况确认元素是否在Shadow DOM中检查页面是否完全加载完成6.2 异步操作超时现象等待元素超时解决方案# 调整全局等待时间 self.set_timeout(30) # 默认是10秒 # 使用更精确的等待条件 self.wait_for_element_present(div.alert-success)6.3 跨域问题处理当测试需要访问多个域名时# 启动浏览器时添加参数 chrome_options webdriver.ChromeOptions() chrome_options.add_argument(--disable-web-security) chrome_options.add_argument(--allow-running-insecure-content)6.4 测试稳定性提升技巧启用重试机制pytest.mark.flaky(reruns2, reruns_delay1) def test_payment(self): ...失败时截图def test_example(self): try: # 测试步骤 except Exception as e: self.save_screenshot(failure.png) raise视频录制需要额外配置# pytest.ini [pytest] seleniumbase_recording true7. 项目经验总结在实际测试Cypress Real World App的过程中有几个关键经验值得分享前后端分离架构的测试需要特别注意API请求的路径和代理配置相对路径比绝对路径更可靠。测试隔离是自动化测试的核心原则之一特别是对于有状态的应用必须在测试之间清理浏览器状态。等待策略需要根据实际业务场景调整金融类操作通常需要更长的超时时间。测试数据管理应该与应用架构解耦通过工厂模式或API预创建数据比UI操作更高效。SeleniumBase的报告功能非常强大合理使用截图、HTML报告和日志能极大提升调试效率。最后分享一个实用技巧在编写测试用例时可以先用self.pause()方法暂停执行然后手动检查页面状态确认无误后再继续自动化流程。这个技巧在调试复杂交互时特别有用。