ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从零搭建Python+Unittest+HTML的UI自动化测试框架实践

从零搭建Python+Unittest+HTML的UI自动化测试框架实践 做UI自动化这事我一开始是拒绝的。不光是我团队里大多数测试同学听到“自动化”三个字第一反应都是稳定吗值得吗投入产出比会不会很惨但真正把一套基于pythonunittesthtml的UI自动化测试框架从零跑到稳定之后我才发现这事没那么玄乎也没那么难。它的核心门槛不在“自动化”本身而在于你愿不愿意先把地基打好。这套框架说白了就三层东西python负责写逻辑unittest负责组织和管理用例html负责输出让人看得懂、领导也看得懂的报告。没有花哨的框架没有复杂的平台依赖每个人都可以在自己的电脑上从零搭起来。这篇文章我想把从环境搭建、目录设计、核心封装到报告生成、持续集成、踩坑记录的完整过程都写出来适合刚接触自动化测试的测试工程师也适合想自己搭一套轻量级框架的小团队参考。1. 项目概述与技术选型思考1.1 为什么选pythonunittesthtml而不是别的组合现在的自动化测试工具和框架五花八门商业的有UFT就是以前的QTP开源的有Robot Framework、pytest、Cypress、Playwright等等。但我最后选了pythonunittesthtml这套组合核心原因就三个字够轻、够稳、够通用。python在测试圈的普及率太高了。哪怕你没系统学过python只要会写一点脚本看看selenium的API几天时间就能上手写用例。而且python的库生态非常完善除了selenium后面想接requests做接口测试、接pytest做更复杂的用例管理、接allure生成报告都是顺理成章的事不会有技术断层。unittest是python标准库自带的测试框架最大的好处是不用额外装东西、不用额外学一套规则。你只要继承TestCase类写一堆以test_开头的方法它就能自动收集、执行、统计结果。很多人觉得unittest没有pytest灵活这是事实。但对于UI自动化来说unittest的“死板”反而是优势——用例的组织结构很规整setUp和tearDown这种钩子方法特别适合处理每个用例都需要的打开浏览器、关闭浏览器动作。不要一上来就追新东西先把标准库用透。html就是测试报告。UI自动化的结果一定是要给人看的。你自己调试的时候可以看控制台输出但项目上线、回归测试、给领导汇报总不能让人家去看一堆cmd窗口滚动。一份结构清晰、有统计信息、有失败截图的HTML报告能让整个工作的价值感完全不一样。1.2 这套框架到底能解决什么问题我见过很多测试团队“自动化”了半年最后只留了一堆没人看的脚本。原因其实不是技术不行而是从一开始就没想清楚框架要解决什么问题。我这套框架的目标非常明确它解决的核心问题有三个第一回归测试的效率问题。一个Web产品每次发版核心流程至少要回归一遍。人工回归2个小时起步自动化跑一遍20分钟搞定而且晚上回家之前点一下执行第二天早上看报告就行。第二用例的管理和可维护性问题。通过Page Object模式封装页面元素和操作页面变了只改一处不用把所有用例全部重写。第三结果的可视化问题。HTML报告里有总用例数、通过数、失败数、错误数、执行时长每个失败用例都能看到堆栈信息、截图和日志定位问题的时间能省一大半。这里要强调一下UI自动化不是用来替代手工测试的。它最适合做的是那些稳定模块的回归验证。你要是拿它去测那种需求天天变、界面天天改的功能那确实会做得很痛苦。后面很多“自动化失败”的项目其实都是栽在选错了测试对象上。2. 环境准备与工程骨架搭建2.1 Python环境与依赖安装先说环境。这套框架最低要求是Python 3.8以上推荐直接用3.10或3.11。如果你电脑上已经装了python可以在命令行里输入python --version确认版本。没装的话去python官网下载安装包装的时候务必勾选“Add Python to PATH”这个选项不然后面在命令行里敲python会提示找不到命令。装完python之后建议顺手把pip的源换成国内镜像不然下载包的时候那速度会让你怀疑人生。Windows下在用户目录下创建一个pip.ini文件Linux和Mac下是pip.conf写入这样的内容[global] index-url https://pypi.tuna.tsinghua.edu.cn/simple trusted-host pypi.tuna.tsinghua.edu.cn这一步是我实测下来非常值得做的一步。我第一次搭环境的时候用默认源下载selenium几MB的包等了七八分钟换完镜像源之后基本是秒下。然后安装核心依赖pip install selenium pip install webdriver-manager pip install html-testRunner这里稍微展开一下为什么用webdriver-manager。以前我们用selenium写脚本需要手动去浏览器驱动官网下载对应版本的chromedriver还得把驱动放到python的Scripts目录里或者指定路径。最坑的是浏览器一升级驱动版本不匹配脚本全部报错又得重新下载。webdriver-manager这个库可以自动检测你本地的浏览器版本自动下载对应的驱动省掉了这些麻烦。html-testRunner是用来生成HTML报告的库。注意一下网上很多教程还在用HTMLTestRunner那个是很多年前的版本了还需要手动下载.py文件。html-testRunner是它的更新版直接pip安装就能用而且生成的报告样式更现代化支持失败截图展示强烈推荐用这个。2.2 工程目录的设计思路一套好用的框架目录结构必须从第一天就设计清楚。不要等到写了几十个用例之后再重构。我常用的目录结构是这样的auto_ui_framework/ │ ├── common/ # 公共模块 │ ├── __init__.py │ ├── base_page.py # 页面基础类封装元素定位、点击、输入等操作 │ ├── config.py # 全局配置读入常量、base_url、超时时间等 │ ├── logger.py # 日志模块 │ └── driver.py # 浏览器驱动初始化 │ ├── pages/ # 页面对象层Page Object │ ├── __init__.py │ ├── login_page.py # 登录页相关元素和操作 │ └── home_page.py # 首页相关元素和操作 │ ├── test_cases/ # 测试用例层 │ ├── __init__.py │ ├── test_login.py │ ├── test_home.py │ └── test_suite.py # 批量执行入口 │ ├── reports/ # HTML报告输出目录 ├── logs/ # 运行日志输出目录 ├── screenshots/ # 失败截图输出目录 ├── requirements.txt # 项目依赖清单 └── run.py # 项目总入口执行整个测试集这个结构的好处是分层清晰每层干每层的事。pages目录放页面元素和操作test_cases目录放业务断言common放公共能力谁跟谁都不纠缠。后面项目变大、用例变多顶多增加几个子目录也不用动结构。有个容易被忽略的点每个目录下都要放一个空文件__init__.py。python的包机制要求目录下必须有这个文件才能被其他模块import进来。忘了这个文件写代码的时候from pages.login_page import LoginPage就会报ModuleNotFoundError很多人在这上面卡了半天。2.3 公共配置与浏览器驱动管理配置这块我习惯单独写一个config.py里面集中放那些可能会变的参数。比如被测系统的base_url、浏览器的宽高、跑用例的默认等待时间、报告和日志的输出路径。这样后面接手项目的人不需要翻遍所有代码去找一个藏在某个用例里的IP地址或者超时时间。import os BASE_URL https://example.com BROWSER_TYPE chrome IMPLICITLY_WAIT 10 # 隐式等待时长秒 PAGE_LOAD_TIMEOUT 30 # 页面加载超时秒 DEFAULT_SCREENSHOT_DIR os.path.join(os.path.dirname(os.path.dirname(__file__)), screenshots) REPORT_DIR os.path.join(os.path.dirname(os.path.dirname(__file__)), reports) LOG_DIR os.path.join(os.path.dirname(os.path.dirname(__file__)), logs)driver.py里的核心逻辑是这样的import os from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service as ChromeService from common.config import BROWSER_TYPE, IMPLICITLY_WAIT, PAGE_LOAD_TIMEOUT class WebDriverFactory: staticmethod def get_driver(): if BROWSER_TYPE.lower() chrome: options webdriver.ChromeOptions() options.add_argument(--start-maximized) # 防止selenium被识别导致登录异常加一个隐藏参数视项目情况 options.add_experimental_option(excludeSwitches, [enable-automation]) service ChromeService(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionsoptions) elif BROWSER_TYPE.lower() firefox: driver webdriver.Firefox() else: raise ValueError(fUnsupported browser type: {BROWSER_TYPE}) driver.implicitly_wait(IMPLICITLY_WAIT) driver.set_page_load_timeout(PAGE_LOAD_TIMEOUT) return driver--start-maximized参数让浏览器启动时直接最大化避免因为窗口大小导致元素被遮挡而定位不到。excludeSwitches这个参数是用来把浏览器顶部的“Chrome正在受到自动软件的控制”这条提示去掉的有些系统对它比较敏感去掉之后更干净。这个是实践里发现的小技巧。3. 核心封装让用例写起来更顺手3.1 页面对象模式POM的具体落地Page Object模式简称POM是UI自动化框架里最核心的设计模式。它的大白话就是你为每一个页面写一个类把页面上的元素定位写在这个类里面把对页面的操作也写成这个类的方法。测试用例本身不直接接触元素定位而是调用这些方法。这样做的逻辑很简单一个登录按钮你可能在十个用例里都用到了。如果每个用例都自己去写driver.find_element(By.ID, login_btn).click()哪天按钮的ID变了你就要改十个地方。但如果你把它封装在LoginPage类的click_login_button()方法里只用改一个类文件就行。我常用的base_page.py大概长这样import time import logging from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from common.config import DEFAULT_SCREENSHOT_DIR class BasePage: def __init__(self, driver): self.driver driver self.logger logging.getLogger(__name__) def find_element(self, locator, timeout10): locator格式为(By.ID, username) 或 (By.XPATH, //input[nameuser]) try: elem WebDriverWait(self.driver, timeout).until( EC.visibility_of_element_located(locator) ) return elem except Exception as e: self._take_screenshot() self.logger.error(f定位元素失败: {locator}, 错误: {e}) raise e def click(self, locator, timeout10): elem self.find_element(locator, timeout) elem.click() self.logger.info(f点击元素: {locator}) def input_text(self, locator, text, timeout10): elem self.find_element(locator, timeout) elem.clear() elem.send_keys(text) self.logger.info(f输入内容: {locator} - {text}) def _take_screenshot(self): timestamp time.strftime(%Y%m%d_%H%M%S) file_path f{DEFAULT_SCREENSHOT_DIR}/fail_{timestamp}.png self.driver.save_screenshot(file_path) self.logger.info(f截图保存到: {file_path}) return file_path所有页面的类都继承这个BasePage。login_page.py就变得非常清爽from selenium.webdriver.common.by import By from common.base_page import BasePage class LoginPage(BasePage): username_input (By.ID, username) password_input (By.ID, password) login_button (By.CLASS_NAME, btn-login) def input_username(self, username): self.input_text(self.username_input, username) def input_password(self, password): self.input_text(self.password_input, password) def click_login(self): self.click(self.login_button)这样写最直观的好处是测试用例读起来就像自然语言一样。你不需要去猜这步在干什么用例的可读性提升了一个档次。3.2 unittest与selenium的结合方式unittest和selenium结合的关键在于怎么巧妙地利用unittest的setUp和setUpClass这类生命周期方法。先写一个base_test.py让所有测试类都继承它import unittest from common.driver import WebDriverFactory class BaseTest(unittest.TestCase): classmethod def setUpClass(cls): cls.driver WebDriverFactory.get_driver() classmethod def tearDownClass(cls): cls.driver.quit() def setUp(self): # 每个用例开始前都回到首页避免用例之间互相影响 self.driver.get(self.base_url) def tearDown(self): # 用例失败时自动截图并挂在日志里 if hasattr(self, _outcome): result self._outcome.result if result.errors or result.failures and id(self) in [id(f[0]) for f in (result.errors result.failures)]: self.driver.save_screenshot(fscreenshots/{self._testMethodName}_fail.png) super().tearDown()这里有几个细节。setUpClass是类级别的初始化整个测试类只启动一次浏览器。setUp是每个用例之前都会跑的放回到首页让用例之间相互独立。tearDown里的那段判断逻辑是检查当前用例是否有失败或错误有的话自动截图。这个功能非常实用后面排查失败原因的时候有截图和没截图的效率完全是两个级别。注意如果你有几十个用例setUpClass只启动一次浏览器执行速度会快很多。但代价是用例之间的耦合度会变高如果前一个用例把页面状态改得乱七八糟后一个用例可能就受影响了。所以一般我建议用例内部或setUp里都要有回到稳定初始状态的逻辑。3.3 三种等待机制到底怎么配合UI自动化里最玄学的问题就是元素定位不稳定。明明手工点一点就出来了自动化脚本一跑就找不到元素。百分之八十的原因出在等待上。selenium里常用的等待方式有三种强制等待、隐式等待、显式等待。强制等待就是time.sleep(2)写起来简单但它是最不推荐的方式。你永远不知道等2秒够不够等多了又白白浪费时间。一个脚本里塞十几个sleep跑一轮下来慢得你怀疑人生。隐式等待是driver.implicitly_wait(10)它告诉浏览器在找不到元素时最多等10秒钟。这个设置一次全局生效代码里到处都不用再写等待。它的缺点是如果某一个页面真的需要等15秒10秒不够用但大部分元素其实秒出又白白多等了。所以一般建议设成一个保守的值比如10秒甚至15秒。显式等待是最灵活、最推荐的。就是前面base_page.py里用到的WebDriverWait它可以针对某一个独立的元素设置独立的等待时间还可以指定等待条件比如元素可见、元素可点击、元素消失等等。我个人的经验是全局设一个15秒左右的隐式等待兜底然后在关键业务操作上再用显式等待做精细化控制。比如登录按钮等到它可点击了再点比如弹窗等到它出现了再操作。这套组合下来脚本稳定性明显上升一个台阶。4. HTML测试报告数据可视化比想象中的重要4.1 HTMLTestRunner的接入与参数配置跑完用例之后没有一份好报告整个框架的可信度就大打折扣。这里我用的是html-testRunner这个库它还兼容Python 3风格也比较偏现代支持把失败的截图直接内嵌到报告里。看一下test_suite.py里怎么组织批量执行和生成报告import unittest import time import os from HtmlTestRunner import HTMLTestRunner from common.config import REPORT_DIR # 手动指定要执行的测试类 from test_cases.test_login import TestLogin from test_cases.test_home import TestHome if __name__ __main__: loader unittest.TestLoader() suite unittest.TestSuite() suite.addTests(loader.loadTestsFromTestCase(TestLogin)) suite.addTests(loader.loadTestsFromTestCase(TestHome)) timestamp time.strftime(%Y%m%d_%H%M%S) report_file os.path.join(REPORT_DIR, fui_test_report_{timestamp}.html) runner HTMLTestRunner( outputREPORT_DIR, report_titleWeb端UI自动化测试报告, report_namefui_test_report_{timestamp}, combine_reportsTrue, add_timestampFalse ) runner.run(suite)combine_reportsTrue可以让多个测试类的报告汇总到一个HTML文件里不然每个类都会单独生成一份报告看起来很碎。report_title和report_name分别控制报告标题和文件名。加时间戳是为了保留历史报告方便后面对比某次版本变更前后用例通过率的变化。跑完之后在reports目录下会生成一个HTML文件直接双击就能在浏览器里打开。报告里会有总用例数、通过数、失败数、错误数、跳过数、运行时长这些统计信息每一行用例的通过状态、错误信息也都列得清清楚楚。4.2 报告里的失败信息怎么写才够用刚搭框架的时候我犯过一个很典型的错误断言写得太粗失败之后根本看不出来是哪个环节出了问题。比如登录用例我只写了self.assertEqual(driver.current_url, https://example.com/home)如果登录失败了报告里只告诉我URL不对但到底是页面没加载出来、按钮没点进去、还是用户名密码错误了完全不知道。后面我给自己定了一个规矩每个关键步骤之后都要有明确的状态校验断言必须带上下文信息。class TestLogin(BaseTest): base_url https://example.com/login def test_login_success(self): self.driver.get(self.base_url) login_page LoginPage(self.driver) login_page.input_username(admin) login_page.input_password(123456) login_page.click_login() # 等待登录成功后的某个特征元素出现 home_page HomePage(self.driver) self.assertTrue( home_page.is_user_info_displayed(), 登录后主页的用户信息区域未显示登录可能失败 ) def test_login_wrong_password(self): self.driver.get(self.base_url) login_page LoginPage(self.driver) login_page.input_username(admin) login_page.input_password(wrong) login_page.click_login() error_msg login_page.get_error_message() self.assertIn(用户名或密码错误, error_msg, f错误提示内容不符实际提示为: {error_msg})这样做之后报告里每一条失败信息都是可追溯的。哪怕是团队里其他人或者刚入职的测试同学来看报告也能很快明白用例在验证什么、失败可能是什么导致的。别小看这一点我见过一个报告里全是“Expected True, got False”的项目那可真叫一个崩溃。4.3 失败重跑和自定义报告扩展验收测试是一个脏活UI自动化跑在改动频繁的浏览器环境里总会遇到偶发性的失败。点一下没点上、动效没走完、网络抖了一下这类问题可能跟代码逻辑没关系纯属环境抖动。我采用的策略是“失败自动重跑一次”。实现起来也不复杂自定义一个unittest的TestResult类或者用现成的库rerun也可以。但更简单的方式是在用例执行层做一次循环class TestCaseWithRetry(unittest.TestCase): _retry_count 2 # 重试次数 def run(self, resultNone): current_frame None for attempt in range(self._retry_count 1): if current_frame: self._outcome None current_frame.clear() result super().run(result) if result.wasSuccessful(): break return result注意这里有个复杂的细节unittest的TestResult对象在重跑时状态是累积的所以每轮重跑前要清理掉上次结果。这实现起来比较绕更稳妥的方案是用pytest的flake-rerun插件来做或者用一个小型装饰器在套件层做外层循环。重跑逻辑要克制不能无限重试。一次失败后重试一次能覆盖大部分偶发问题重试两次以上效果反而不明显还容易把真正的bug掩盖掉。一路重试到绿那不是自动化测试那是自欺欺人。5. 测试执行与持续集成5.1 批量执行、挑选用例与并发问题最初跑用例是一个套件把所有用例全跑一遍。但当用例数量涨到一两百个之后问题就来了跑一轮要一小时一个小改动引发的回归就要等这么久太慢了。解决思路有两个方向一个方向是按影响范围挑选用例比如改动了登录模块就只跑登录相关的用例另一个方向是并发执行多个浏览器同时跑不同的用例。unittest本身不支持并发但可以借助unittest的TestSuite配合多线程自己写一个并发启动器。大概思路就是把测试类按类分配到不同的线程里每个线程启动一个浏览器进程。这里有一个非常关键的坑如果你用了setUpClass去共享同一个浏览器驱动那么不同线程之间的driver每次都要重新创建不要试图用一个全局driver变量绕过去selenium的WebDriver不是线程安全的强行共享会引发各种诡异异常。还有一套更稳的方案就是把测试执行命令交给pytest。pytest虽然和unittest的写法不同但它可以直接运行unittest风格的用例并且自带pytest-xdist插件支持分布式并发执行。命令行里加一个-n 4就能用4个并发进程跑用例。如果你的用例彼此独立这一步优化的效果会非常显著。5.2 接入Jenkins实现定时回归写好的自动化框架如果不接入持续集成系统那就只能算是本地工具谈不上真正的测试基建。我这里以Jenkins为例说一下关键接入点。Jenkins里新建一个自由风格的项目源码管理选择你的git仓库构建环境里选上“Add timestamps to the Console Output”这样看日志的时候能知道每步的耗时。最核心的是构建命令cd /your/project/path python -m pip install -r requirements.txt python test_cases/test_suite.py构建后操作里选择“Publish HTML reports”填上报告目录和报告文件。这样每次构建结束后首页就会有一个可以点击的“Latest Test Report”链接点开就是一份完整的HTML报告。为了让报告更容易浏览我还会在构建后加一个邮件通知。用Email Extension Plugin把HTML报告作为附件发出去或者只发一个包含测试结果摘要的邮件。这样每天早上一封邮件团队所有人都能看到昨晚回归的情况。接入Jenkins后的运行时间建议放在晚上。白天电脑办公、网络波动、浏览器弹窗都可能干扰测试晚上环境干净很多执行结果也更可信。第二天早上上班第一件事就是打开Jenkins看报告有问题第一时间反馈给开发这种方式已经被验证非常高效。5.3 执行策略定时、轮询还是人工触发这里想多说一句关于“到底该怎么触发UI自动化测试”的建议。很多人搭完框架就兴奋地设了个每天晚上10点定时跑的任务结果每天早上都收到一堆失败报告一个月后大家的习惯就变成了“看到这个邮件都不打开了”。这会直接毁掉自动化测试在团队里的信任度。我个人的经验是循序渐进。一开始只做“人工触发”也就是只在发版前或需求提测的时候手动点一下Jenkins执行先跑一周把那些天天失败的用例修正一遍稳定之后再改成“定时触发”比如每天夜里回归一次最后再考虑在代码合并流水线里加一个“冒烟测试”阶段只在push到主分支时跑核心用例跑挂了就直接拦住合并请求。要让团队相信自动化结果先要做到自动化的报告可靠。一上来就追求全自动化和高频率往往是压垮项目的最后一根稻草。6. 踩坑记录与问题排查速查表6.1 元素定位不稳定的深层原因UI自动化最大的敌人就是“昨天还能跑今天突然挂了”。这类问题的排查我基本遵循一个固定的思路先看报告里的错误信息是NoSuchElementException、ElementClickInterceptedException还是TimeoutException。NoSuchElementException是找不到元素最常见的原因是定位表达式变了或者元素还没加载出来。先换成显式等待看能不能解决解决不了就去页面看看是路径变了还是id变了。ElementClickInterceptedException是元素被什么东西挡住了跑脚本的时候经常遇到弹窗、浮层、Cookie提示条。解决办法是先关掉这些遮挡元素或者用WebDriverWait等待遮挡元素消失。TimeoutException多发生在显式等待超时比如等待一个弹窗出现但一直没出现那就要考虑是不是某个前置操作没成功业务逻辑本身报错了。这时候报告里有没有截图就派上大用场了。除了这些问题还有一类很隐蔽的坑同一个页面元素在开发环境、测试环境、正式环境里的定位完全不同。这个基本只能靠配置文件里分环境设定不同的定位符来解别无他法。6.2 WebDriver版本不匹配与浏览器更新陷阱用webdriver-manager解决了大部分驱动问题但偶尔还是会遇到“SessionNotCreatedException: This version of ChromeDriver only supports Chrome version xxx”。这个问题通常发生在浏览器自动升级之后。ml 解决方案很简单把webdriver-manager升级到最新版然后清理掉缓存的本驱动pip install --upgrade webdriver-manager webdriver-manager clean另外尽量不要在自动化服务器上开启浏览器的自动更新。Windows系统可以通过组策略关闭Chrome自动更新避免夜里升级完了第二天起来自动化全家挂掉。这些看起来细枝末节的东西执行起来都是影响框架稳定性的关键。总之一句话自动化环境里的浏览器版本要跟依赖的webdriver版本严格保持一致。6.3 并发执行与数据隔离做并发执行的时候测试数据隔离是另一个大坑。假设你有两个用例同时登录同一个账号一个用例改了密码另一个用例立刻受影响再比如两个用例同时创建一个相同名称的订单第二个用例就创建失败了。不解决数据问题并发执行就是灾难现场。我的习惯是每个测试用例都用独立的测试数据。要么在用例初始化时通过接口造数据要么把测试账号池化每个并行进程分配一个独立的账号。实在躲不开共用数据的场景就只能把这些用例放到串行队列里跑牺牲一点速度换来稳定。6.4 常见问题排查速查表这里整理一下我在实践中最常遇到的十来个问题做成一张速查表。现象可能原因排查与解决方法启动脚本就不停报session not created浏览器和webdriver版本不匹配升级webdriver-manager并清理缓存一直找不到某个元素页面加载太慢或元素在iframe里先用显式等待检查是否要switch_to_iframe按钮点击没反应页面上的元素被浮层遮挡查看报告截图先关闭遮挡元素再点击点击后进入不了下一页面页面跳转有时间窗口用WebDriverWait等待页面特征元素出现用例偶发失败网络波动、动效未结束、环境脏数据考虑失败重跑机制先重试一次报告里没有截图失败时driver状态已经异常在tearDown中捕获异常后再截图并发跑时数据相互干扰用例没有独立测试数据引入测试账号池或隔离测试数据集本地能跑Jenkins上跑不了Jenkins服务账号没有图形界面或权限受限配置xvfb或改用headless模式登录状态总是失效浏览器上下文没共享登录token没保存用同一浏览器实例串行执行或者统一走接口处理登录执行时间越来越长用例越来越多HTTP慢请求没优化引入并发执行或精简用例集7. 写在最后的实操心得这套pythonunittesthtml的UI自动化测试框架从环境搭建到跑出第一份报告正常一两天就能搞定。整体项目做下来我个人的体会是框架只是手段质量和效率才是目的。任何花里胡哨的技术方案如果不能帮团队在日常回归中节省时间、提前发现问题那它就是个“面子工程”。实际使用中还有几点想额外提一下。第一测试框架的代码本身也要写注释、做评审它和业务代码一样需要维护第二不要过度封装封装过深会让脚本变得特别“绕”排查问题的时候要跳好几层才找到真正的逻辑第三UI自动化需要持续调优运行一段时间后要定期去看报告里的失败用例把那些因为环境原因反复失败的用例修掉或者标成跳过保持报告的真实置信度。最后分享一个小技巧我在框架里加了一个run.py的入口可以直接用命令行参数指定跑哪个模块、是否开启重试、是否生成报告。这样测试同学不需要了解代码只要记住几个命令就能执行用例。让工具尽可能简单让使用者尽可能省心这才是自动化测试框架能真正落地和长期跑下去的关键。
返回列表