新闻详情

新闻详情

首页 / 资讯中心 / 详情

Selenium自动化测试框架重构实战:从分层设计到稳定落地

发布时间:2026/9/9 22:01:17来源:尧图网络
Selenium自动化测试框架重构实战:从分层设计到稳定落地
1. 重构前的框架诊断先搞清楚为什么散再谈怎么改接手这个Selenium自动化测试框架重构的需求之前我先把老框架从上到下翻了一遍。老实说很多团队遇到的情况都差不多用例散落在各个模块里元素定位写得五花八门有人用class_name有人用xpath硬编码浏览器驱动配置散在每台机器上跑一次全量回归全看机器心情。举个最典型的例子老框架里有一条登录用例定位用户名输入框用的是find_element_by_id(username)另一条用例里同一个输入框换成了find_element_by_xpath(//input[nameusername])到了第三条用例里可能又变成了CSS选择器。光是统一这类定位方式就能让人头皮发麻。而且一旦前端改了样式或DOM结构等待你的就是几十条用例一起飘红排查起来逐个点开看报错信息效率极低。重构的第一步从来不是写代码而是先做诊断。我拉取了近三个月的测试报告和执行日志发现几个明显问题第一用例执行时间波动大同样的用例集有时候跑25分钟有时候跑50分钟。排查下来是因为老框架里大量使用sleep(5)这种硬等待页面加载快的时候白白等页面加载慢的时候又等不够稳定性完全不可控。第二元素定位失败率异常高。统计了一个月的失败用例超过60%的失败原因是NoSuchElementException。根因在于老框架没有统一的显式等待策略也没有对公共组件做封装前端稍作调整脚本就跟着报废。第三几乎没有用例间的数据隔离。用例A登录后改了用户昵称用例B再登录时就发现断言数据和预期不一致导致偶发性失败。这类问题最恶心单跑全过回归必挂。第四老框架没有任何报告输出和失败截图机制。用例失败后只能靠控制台日志去猜连个截图都没有排查成本极高。这些问题的根源不是Selenium本身而是框架缺少分层设计和统一规范。Selenium只是一套Web自动化操作库它可以做的事情很多但如果你没有一个良好的架构去约束用法它的灵活反而会成为灾难。所以重构的核心思路就四个字分层、收敛。把元素定位、页面对象、业务操作、用例执行、报告输出逐层剥开每层只干自己该干的事这样后续无论是维护还是扩展都会轻松一个量级。2. 框架重构的三大设计决策PO模式、配置文件驱动、数据与脚本分离2.1 页面对象模式PO到底解决什么问题页面对象模式Page Object Model是Selenium自动化测试里最经典、也最值得优先落地的设计模式。它的核心思想是把页面上的元素定位和页面行为封装成一个类测试用例只跟这个类的业务方法打交道不直接接触Selenium的查找元素API。我这么说可能还是有点抽象直接看一个对比。重构之前用例代码长这样def test_login(): driver.find_element(By.ID, username).send_keys(test_user) driver.find_element(By.ID, password).send_keys(123456) driver.find_element(By.ID, login_btn).click() time.sleep(3) assert 欢迎 in driver.find_element(By.CLASS_NAME, user_info).text代码本身不难理解但如果登录页面有30个用例都要执行登录操作这五行代码就要复制粘贴30遍。一旦前端改了密码框ID你就得全局搜索替换漏一处就是一次线上事故。重构之后我把登录页封装成一个类class LoginPage: def __init__(self, driver): self.driver driver self.username_input (By.ID, username) self.password_input (By.ID, password) self.login_button (By.ID, login_btn) self.user_info (By.CLASS_NAME, user_info) def login(self, username, password): self.driver.find_element(*self.username_input).send_keys(username) self.driver.find_element(*self.password_input).send_keys(password) self.driver.find_element(*self.login_button).click() def get_logged_in_username(self): return self.driver.find_element(*self.user_info).text然后用例里只需要写def test_login(login_page): login_page.login(test_user, 123456) assert 欢迎 in login_page.get_logged_in_username()这一层封装的价值在于页面变了只改页面类和用例无关。用例的编写者只需要知道业务方法叫什么、传什么参数不需要关心页面结构。这不仅仅是省代码量的问题而是把维护成本从“全局搜索替换”收敛到了“只改一个文件”。做PO封装时我还有一个建议元素定位元组化。上面看到我用(By.ID, username)这种元组来定义定位器而不是直接写find_element(By.ID, username)。好处是定位器和查找动作分离你可以在定位器层面做统一管理比如加日志、加等待、加重试这些在后面的核心模块里会详细展开。2.2 配置文件驱动把环境差异从代码里剥离出来老框架里最常见的场景是测试环境的URL写死在conftest.py里某个同事本地调试时临时改成自己的地址然后忘记改回来提交代码后CI直接挂掉。这种问题几乎每个做自动化测试的团队都遇到过。配置文件驱动的思路是把环境地址、浏览器类型、超时时间、账号信息这类易变内容全部抽到配置文件里。我用的是YAML格式因为它的可读性比JSON好也支持注释。# config.yaml environment: base_url: https://test.example.com browser: chrome timeouts: implicit_wait: 5 explicit_wait: 10 page_load_timeout: 30 accounts: admin: username: admin_test password: admin_pwd normal: username: user_test password: user_pwd report: screenshot_on_failure: true screenshot_dir: screenshots然后在代码里做一个配置加载模块用yaml.safe_load()读取文件再通过一个全局配置对象访问import yaml from types import SimpleNamespace def load_config(pathconfig.yaml): with open(path, encodingutf-8) as f: raw_config yaml.safe_load(f) return Config(raw_config) class Config: def __init__(self, raw): self.base_url raw[environment][base_url] self.browser raw[environment][browser] self.timeouts SimpleNamespace(**raw[timeouts]) self.accounts raw[accounts]这样做的直接收益是换环境或者换账号的时候只需要改配置文件不需要改任何代码。而且你可以针对不同环境准备多套配置比如config_test.yaml、config_staging.yaml启动时通过命令行参数指定加载哪一套。配合pytest的--env参数我一般是这么设计的def pytest_addoption(parser): parser.addoption(--env, actionstore, defaulttest, help选择测试环境: test/staging) def pytest_configure(config): env config.getoption(--env) env_config load_config(fconfig_{env}.yaml) config.env_config env_config跑测试的时候只需要pytest --envstaging框架就能自动切换配置。这套机制目前在我接触过的项目里都是高收益低成本的改善而且对后续做多环境回归、接口联调都有帮助。2.3 数据与脚本分离Excel、JSON还是直接写在代码里数据驱动是自动化测试框架的又一个关键设计点。它的意义在于把测试数据和测试逻辑解耦让同一个用例可以用多组数据去跑实现“一次编写多组数据执行”的效果。在数据格式的选择上我对比过三种方案数据格式优点缺点适用场景JSON结构清晰嵌套方便不支持注释写起来啰嗦复杂结构数据、API测试YAML可读性最好支持注释缩进容易出错配置文件、简单测试数据Excel运营/产品也能维护格式解析依赖库跨平台问题数据量大、非技术维护数据我在这个框架里主要用了YAML来管理测试数据因为Selenium UI自动化的测试数据通常不会太复杂。比如登录模块的异常场景测试数据# testdata/login_data.yaml test_login_success: username: admin_test password: 123456 expected: 欢迎回来 test_login_wrong_password: username: admin_test password: wrong_pwd expected: 用户名或密码错误用pytest的参数化机制直接把这些数据注入用例import pytest import yaml with open(testdata/login_data.yaml, encodingutf-8) as f: login_data yaml.safe_load(f) pytest.mark.parametrize(case_name,data, login_data.items()) def test_login(case_name, data, login_page): login_page.login(data[username], data[password]) assert data[expected] in login_page.get_page_message()数据驱动还有一层更深的含义就是用例里尽量不写硬编码数据。比如一个购买流程的用例购买的商品数量、收货地址、支付方式这些数据都应该从数据文件中读取。这样当测试环境的数据需要刷新时你只需要改数据文件而不是翻代码。3. 核心模块的落地实现从Selenium原生态到工程化封装3.1 自定义元素定位封装明明等到了为什么还是找不到元素Selenium元素定位失败一半以上都和同步问题有关。页面还没加载完元素还没渲染出来脚本就去找元素了自然找不到。老框架里的做法是sleep(5)简单粗暴但前面说过效果极差。重构时我封装了一个元素操作的基类把显式等待内置到了查找元素的动作里。这是整个框架里性价比最高的一个模块。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver def find_element(self, locator, timeout10): element WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) return element def find_clickable_element(self, locator, timeout10): element WebDriverWait(self.driver, timeout).until( EC.element_to_be_clickable(locator) ) return element def input_text(self, locator, text, timeout10): element self.find_element(locator, timeout) element.clear() element.send_keys(text) def click(self, locator, timeout10): element self.find_clickable_element(locator, timeout) element.click()这里有几个容易踩的细节值得展开说说。第一个是presence_of_element_located和visibility_of_element_located的区别。前者只判断元素是否出现在DOM树里不判断是否可见后者要求元素存在于DOM中且可见。如果某个动画元素还在渐入过程中用presence定位到了但点击时可能报“element not interactable”错误。所以点击操作我建议用element_to_be_clickable它内部同时检查了可见性和可用性。第二个是element.clear()的重要性。很多自动化脚本在输入前不清理输入框如果上一次运行留下了历史文本输入的内容就会拼接在后面导致后续断言失败。虽然大多数情况下send_keys会覆盖原有内容但遇到带有默认值的输入框就必须先clear。第三个是输入框的类型兼容问题。某些前端框架比如富文本编辑器、自定义下拉框的输入框不是原生的input标签直接send_keys可能无效。这时候需要先点击触发输入状态再通过ActionChains模拟键盘输入。3.2 浏览器驱动管理别让你的脚本因为版本号崩溃老框架里浏览器驱动是各人自己下载的版本参差不齐经常出现“我本地能跑CI上跑不了”的情况。重构时我统一用了webdriver-manager这个库它会自动匹配本机浏览器版本并下载对应驱动彻底告别手动管理驱动的痛苦。from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service def create_driver(browserchrome, headlessFalse): if browser chrome: options webdriver.ChromeOptions() options.add_argument(--start-maximized) options.add_argument(--disable-gpu) if headless: options.add_argument(--headlessnew) options.add_argument(--no-sandbox) options.add_argument(--disable-dev-shm-usage) service Service(ChromeDriverManager().install()) return webdriver.Chrome(serviceservice, optionsoptions)要注意的是无头模式headless下有些页面行为会和有界面时不一样比如弹窗、文件下载、某些CSS动画。所以CI环境跑无头模式时如果你发现某些用例在本地过、CI挂优先怀疑无头模式的兼容性问题。我自己的做法是CI上保留无头模式跑冒烟用例全量回归还是在专门的执行节点上有界面运行。另外启动参数里的--disable-dev-shm-usage和--no-sandbox在Docker容器里基本是必加的不加的话Chrome经常崩溃报错信息也很隐蔽一般是“DevToolsActivePort file doesnt exist”或者直接进程退出。3.3 失败自动重试机制稳定性和运行时间我全都要UI自动化最怕的就是偶发性失败页面卡了一下、网络抖了一下、某个接口响应慢了一拍脚本就挂了。如果每次都因为这类原因重跑全量时间成本受不了如果装作没看见频繁的失败报告又没人愿意看。我的方案是加一个基于pytest-rerunfailures插件的重试机制同时对重试的场景做区分。很多团队做重试是一刀切所有用例都重试两次这其实是错的。如果一条用例因为产品bug稳定失败重试多少次都是徒劳只会让整个执行时间平白无故增加一倍。更合理的做法是明确区分哪些用例可以重试涉及页面跳转、异步加载的用例可以重试因为网络波动影响大涉及数据断言、数据库校验的用例不建议重试因为数据状态可能已经被前一次运行污染涉及文件上传、下载的用例不建议重试失败后重复操作容易产生垃圾文件在pytest中可以通过mark标记来控制重试策略pytest.mark.flaky(reruns2, reruns_delay2) def test_order_flow_can_be_completed(): ...重试间隔设置了2秒为了让页面有充分时间恢复。同时我还配了一个统计逻辑如果同类失败在同一个页面对象上连续出现就直接跳过重试并标记为“疑似环境故障”避免无效循环。3.4 测试报告与失败截图让失败一眼就能定位UI自动化测试的调试成本很大程度上取决于报告的质量。老框架里用例失败后只能在控制台看traceback连元素截图都没有遇到前端样式问题根本无从下手。重构时我把报告方案定为pytest allure。allure的报告确实漂亮交互也好在测试领域几乎是事实标准。但更重要的是我在fixture里加了失败自动截图和日志收集的机制。import allure import datetime pytest.hookimpl(hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield report outcome.get_result() if report.when call and report.failed: driver item.funcargs.get(driver) if driver: timestamp datetime.datetime.now().strftime(%Y%m%d_%H%M%S) screenshot_path fscreenshots/{item.name}_{timestamp}.png driver.save_screenshot(screenshot_path) allure.attach.file( screenshot_path, name失败截图, attachment_typeallure.attachment_type.PNG )这个hook的核心逻辑是任何一条用例失败时从fixture里拿到driver实例保存当前页面截图并附到allure报告里。这样你在查看失败报告时不再是一行冷冰冰的异常信息而是能看到失败时刻页面的真实状态——是弹窗没弹出来还是元素被遮挡还是页面压根就报错了一目了然。除了截图我还会把失败时刻的页面源码一起保存到allure里用allure.attach塞一个HTML附件。有些问题是截图看不太出来的比如某个元素的属性值不对这时候直接看DOM源码就能找到线索。3.5 日志规范贯穿整个执行过程的第一手线索日志在自动化测试框架里经常被忽略但遇到问题时一份规范的日志能帮你省接近一半的排查时间。我在重构时统一用了logging模块配合一个全局配置让每个关键动作都有迹可循。import logging import sys def setup_logging(levellogging.INFO): logger logging.getLogger(autotest) logger.setLevel(level) formatter logging.Formatter( %(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s ) console_handler logging.StreamHandler(sys.stdout) console_handler.setFormatter(formatter) file_handler logging.FileHandler(logs/autotest.log, encodingutf-8) file_handler.setFormatter(formatter) logger.addHandler(console_handler) logger.addHandler(file_handler) return logger在BasePage里每个操作动作都打一条info日志def input_text(self, locator, text, timeout10): element self.find_element(locator, timeout) element.clear() element.send_keys(text) logger.info(f输入文本: {text} 到元素: {locator})不要小看这一行日志。当用例失败时你可以从完整日志里看到最后一步操作是什么然后结合截图和页面源码基本能还原整个失败链条。4. 框架的完整使用示例从conftest到一条业务用例的全链路4.1 conftest.py里的fixture怎么组织conftest.py是pytest的核心机制之一也是整个框架的装配车间。在这个文件里定义的所有fixture可以被测试目录下的所有用例自动使用不需要import。我的conftest.py里主要包括三个基础fixturedriver、base_page、login_page以及各业务模块的页面对象fixture。import pytest from selenium import webdriver from pages.login_page import LoginPage from pages.home_page import HomePage pytest.fixture(scopeclass) def driver(request): env_config request.config.env_config browser env_config.browser drv create_driver(browserbrowser) drv.get(env_config.base_url) yield drv drv.quit() pytest.fixture() def login_page(driver): return LoginPage(driver) pytest.fixture() def home_page(driver): return HomePage(driver)这里有一个scope的选择问题。driver用scopeclass是因为大多数情况下一个测试类里的多个方法共享同一个浏览器会话是合理的能大幅减少启动和销毁浏览器的开销。如果每条用例都新建浏览器几十条用例跑下来光是浏览器启动关闭的时间就占了三分之一。但如果你有某些用例必须隔离浏览器状态比如测试多标签页、测试不同用户权限就需要单独处理。我一般通过pytest.fixture(scopefunction)再额外定义一个new_driver的fixture来覆盖而不是全框架一刀切。另外driver的退出放在fixture的teardown里即yield之后的代码这样无论用例是pass还是fail浏览器都会被正确关闭不会因为用例异常导致浏览器进程泄漏。4.2 一条完整用例的写法写起来像在描述业务需求框架搭好后一条用例写起来应该非常顺畅。我给团队定的标准是用例的代码结构应该像在描述业务操作而不是在写Selenium API调用。以“用户成功下单”为例import allure allure.feature(订单模块) allure.story(用户下单) class TestOrderFlow: allure.title(测试用户从登录到下单的完整流程) def test_user_complete_order(self, login_page, home_page, product_page, cart_page): # 1. 登录 login_page.login(admin_test, 123456) # 2. 进入商品详情页加入购物车 home_page.enter_product(product_name无线鼠标) product_page.add_to_cart() # 3. 进入购物车提交订单 cart_page.submit_order() # 4. 断言 assert cart_page.is_order_success(), 订单提交失败你看这段用例里没有出现任何一个元素定位的API。所有操作都被封装到了页面对象里用例编写者只需要关心业务流程。这就是PO模式的价值——用例层只做三件事调用业务方法、传参数、断言结果。从一个团队的分工角度看这套设计还有一个额外的好处用例编写工作可以交给业务熟悉但代码能力一般的人来做页面对象的封装由技术强的核心人员完成。两者并行不悖效率会比所有人混在一起写快很多。4.3 用例之间的数据隔离与依赖管理UI自动化用例最讨厌的问题就是用例之间的相互干扰。我在重构时制定了三条规则第一每条用例执行前把测试数据恢复到初始状态。最简单的方式是使用setup_method在用例执行前通过数据库连接或API调用重置数据。UI自动化虽然主要是验证前端但测试数据的预置和后清理通过API来做远比走UI高效。第二公用数据只读不修改。比如测试环境的公共账号只用来登录和执行查询类操作绝不用它去修改数据。需要修改数据的场景必须创建独立的数据副本。第三用例执行顺序不依赖任何其他用例的产物。这就要求用例是自包含的需要的测试数据要么在前置步骤中创建要么通过数据文件单独准备。这三条规则落实下来最直接的变化就是你可以放心地用pytest -n auto开启多进程并行执行而不用担心用例之间互相踩踏。4.4 Pytest配置一条命令跑出高质量报告最后一步是pytest的配置文件把这些机制串起来。我在项目根目录下放了一个pytest.ini核心配置如下[pytest] testpaths testcases python_files test_*.py python_classes Test* python_functions test_* addopts -v -s --alluredirreports/allure markers flaky: 标记可能偶发失败的用例 smoke: 冒烟测试用例 regression: 全量回归用例使用的时候日常开发调试就跑pytest testcases/test_login.py提交前跑冒烟测试pytest -m smoke全量回归就是pytest -m regression --envstaging。生成allure HTML报告就两条命令pytest --alluredirreports/allure allure generate reports/allure -o reports/html --clean然后浏览器打开reports/html/index.html就能查看完整的执行报告。5. 实战优化过程中踩过的坑与排查实录5.1 元素明明存在click却报错“element not interactable”这是重构后遇到最多的一个问题。现象是定位器明明找到了元素但点击时Selenium抛WebDriverException: element not interactable。排查过程是这样的先看截图发现元素在页面上确实是可见的。再看页面源码发现这个按钮被一个透明的遮罩层覆盖了。那是一个loading动画的残留元素它虽然透明了但还在页面上占据了位置。解决方案是点击前先判断元素是否可点击而不是仅仅判断是否存在。我在BasePage里加了find_clickable_element方法内部通过EC.element_to_be_clickable来等待。但如果遮罩层一直没有消失element_to_be_clickable会一直等待到超时。所以更彻底的解法是在点击前先尝试关闭或跳过遮罩def safe_click(self, locator, timeout10): self.hide_overlay_if_exists() element self.find_clickable_element(locator, timeout) self.driver.execute_script(arguments[0].scrollIntoView();, element) element.click() def hide_overlay_if_exists(self): try: overlay self.driver.find_element(By.CLASS_NAME, loading_overlay) if overlay.is_displayed(): self.driver.execute_script(arguments[0].style.displaynone;, overlay) except NoSuchElementException: pass这个方法属于“实战向”的解法。虽然用JS强制隐藏遮罩层不算优雅但在UI自动化里你面对的是页面真实的状态有时候绕过问题比解决问题更高效。5.2 页面跳转后旧元素引用变成“stale element reference”另一个高频报错是StaleElementReferenceException。原因是页面经过了跳转或局部刷新之前定位到的元素已经不在DOM中或者被重新渲染了但你的代码里还持有那个旧的元素对象。最经典的场景是点击“下一页”分页后下一页的按钮元素实际上是被重新创建的你用旧引用去点击就会报这个错。解决思路是给元素查找动作加上自动重试。我封装了一个稳定的点击方法def retrying_click(self, locator, timeout10, retries3): for attempt in range(retries): try: element self.find_clickable_element(locator, timeout) element.click() return except StaleElementReferenceException: logger.warning(f第{attempt1}次点击失败元素已过期重新定位) continue raise Exception(f重试{retries}次后仍然无法点击元素: {locator})这个方法的效果是当元素过期时重新定位一次而不是直接让用例失败。多次重试后如果仍然失败说明页面确实有问题这时候再报错也不迟。从原理上讲Selenium的WebElement对象本质上是一个远端引用每一次查找都会在浏览器端生成新的session元素句柄。页面刷新后旧句柄失效所以“每次操作前重新查找元素”是最稳妥的做法。团队里后来有人直接把所有find_element都替换成了带重试的版本虽然多了一点点性能开销但换来了极稳定的执行结果。5.3 CI环境里无头模式跑不过本地却能过这个问题的排查过程可以写一个小专题。现象是本地开发环境跑用例全绿推到CI上跑却随机挂一部分而且每次挂的用例还不一样。打开截图一看页面显示的是空白或加载中状态。用排除法一个个试最终发现是CI执行机的资源配置太低Chrome启动无头模式时默认的资源占用策略太激进再加上并发执行多个用例CPU和内存直接被榨干页面加载超时。解决方案分两步第一步在Chrome options里加上资源限制参数options.add_argument(--disable-extensions) options.add_argument(--disable-background-networking) options.add_argument(--disable-default-apps) options.add_argument(--disable-sync) options.add_argument(--window-size1920,1080)第二步把并行执行的进程数降下来。pytest-xdist的-n参数原来设置的是CPU核心数后来改成-n 2。实测执行时间虽然多了几分钟但稳定性提升了不止一个档次。我的结论是对于UI自动化稳定压倒一切。CI执行环境要有合理的资源预算一味追求并发只会把稳定性拖垮。5.4 踩过坑之后的架构微调把等待策略集中管理经过上面这些坑之后我把框架里所有等待时间的配置全部抽到了config.yaml里不同级别的页面加载有不同的超时设置等待类型推荐超时适用场景隐式等待5秒全局兜底元素查找前的等待显式等待10秒通用元素的出现/可点击等待页面加载等待30秒首次打开页面、大页面加载接口响应等待15秒页面AJAX请求、异步数据加载同时我建议团队写了一个通用方法wait_for_page_load在每次页面跳转后调用确保页面进入稳定状态再继续操作def wait_for_page_load(self, timeout30): current_url self.driver.current_url WebDriverWait(self.driver, timeout).until( lambda d: d.execute_script(document.readyState) complete ) WebDriverWait(self.driver, timeout).until( lambda d: current_url ! d.current_url or len(d.find_elements(By.TAG_NAME, img)) 0 )这个方法不算完美但它能挡住大部分因为页面没加载完就继续操作导致的偶发失败。6. 框架重构完成后回头看哪些事情真正带来了效率提升框架上线跑了一段时间之后我对比了一下重构前后的数据用例总数从80条增加到150条但全量回归的执行时间从原来的40分钟降到了22分钟稳定率从80%出头提升到了95%以上。这个结果说明重构的核心价值不是新用了什么技术而是把混乱的写法统一成了有约束的架构。关于Selenium本身我还想多说一句。很多人觉得Selenium“过时了”现在流行Playwright、Cypress之类的新工具。但我在实际项目中的感受是Selenium依然是Web自动化生态里兼容性最好、社区资料最全的方案尤其是在多浏览器兼容测试这个场景Selenium WebDriver对Chrome、Firefox、Edge、Safari的支持依然是最稳的。如果你所在团队的自动化测试还停留在“脚本能跑就行”的阶段我建议你从这个框架里先挑三样做起来第一把元素定位统一封装进BasePage第二用显式等待替换所有sleep第三配上allure报告和失败截图。这三步做完你的自动化测试项目就已经比原来稳定一大截了。项目后续的扩展方向也很明确。一个是把接口测试和UI测试融合到同一套pytest框架里共用配置和数据管理机制另一个是接入CI的流水线让每次代码提交后自动触发冒烟测试并在MR页面上生成测试报告链接。只要基础的地基打好了往外长东西是水到渠成的事情。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

C语言复合字面量完全指南:C99语法、存储期与悬垂指针陷阱 2026/9/9 22:37:27

C语言复合字面量完全指南:C99语法、存储期与悬垂指针陷阱

说实话,我第一次在别人代码里见到 (struct point){ .x 10, .y 20 } 这种写法时,第一反应是“这玩意是什么?C语言什么时候能这样写了?”查了标准才发现,这是 C99 引入的复合字面量(compound literal&…

阅读更多 →
战地医疗AI系统测试实战:从环境模拟到失效安全设计的关键经验 2026/9/9 22:37:27

战地医疗AI系统测试实战:从环境模拟到失效安全设计的关键经验

炸现场里急救帐篷的灯光通常不够亮,而且总在晃。我盯着屏幕上那个分割模型的输出,血泊里一块弯折的金属碎片被识别成了“骨折断端”。这个错误如果发生在常规诊断场景,顶多是让医生多看一眼CT,问题不大;但如果发生在这…

阅读更多 →
代码覆盖率提升实战:从统计口径到门禁落地 2026/9/9 22:37:27

代码覆盖率提升实战:从统计口径到门禁落地

代码覆盖率这事,圈子里一直有个争论:有人觉得它是衡量测试质量的黄金指标,有人觉得它就是个自欺欺人的数字游戏。我做了这么多年开发和测试基建,两个极端都见过。有一种团队,覆盖率定在80%,大家天天为了补用…

阅读更多 →
No module named ‘utils‘报错别急着pip install,先查这三点 2026/9/9 22:37:27

No module named ‘utils‘报错别急着pip install,先查这三点

一看到 No module named utils,先别急着 pip install老实说,这大概是 Python 社区里被问得最多、又最容易被“误诊”的报错之一。报错文本只有一行:ModuleNotFoundError: No module named utils,但真正的问题往往不是“缺一个叫 u…

阅读更多 →
从最优控制到轨迹规划:倒立摆上翻与车辆路径规划实践 2026/9/9 22:37:27

从最优控制到轨迹规划:倒立摆上翻与车辆路径规划实践

最优控制、轨迹规划、倒立摆上翻控制、车辆运动学约束路径规划、离散点参数化——这些东西很多人是分开学的,但实际做项目时会发现它们全串在一条线上:规划层算出一条可执行路径,控制层负责把它跑出来,中间还夹着一堆离散点怎么变…

阅读更多 →
从冒泡到哈希:排序与查找算法实战全解析 2026/9/9 22:34:26

从冒泡到哈希:排序与查找算法实战全解析

排序和查找算法,是所有写代码的人绕不开的两座山。从大学期末考到社招技术面,从给Excel里的IP地址排个序到在上亿条日志里定位一条记录,背后翻来覆去就是这么几个经典套路。我从2013年开始正经写项目,到现在手写过冒泡排序、快速排…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞