pytest接口自动化进阶:fixture、参数化与断言实战
1. 为什么pytest能成为接口自动化测试的首选框架做接口测试的人应该都有一个感受刚开始用Python写接口用例时最顺手的就是requests库发请求、断言返回值但写着写着就发现代码越来越乱。同一个接口要测十几种参数组合每个用例都复制粘贴一份请求代码登录态要反复获取每个模块都要重新写一遍测试报告更是简陋到只能在控制台看输出领导和同事问起测试结果时只能甩出一张Excel表格。于是很多人开始寻找一个能解决这些痛点的测试框架。市面上的选择其实不少但我个人在实际项目中用得最顺手、也最能提升效率的还是pytest。这个结论不是凭空来的。我在几个不同类型的项目中都做过接口自动化落地有纯后端微服务的接口测试有APP端的接口回归还有数据中台的分层校验。对比下来pytest在fixture复用机制、参数化数据驱动、断言失败信息可读性这三个维度上都明显优于其他框架。这篇是pytest接口测试系列的第二篇。上一篇主要讲了pytest的基础用法和接口测试的入门实践包括如何安装环境、如何编写第一个用例、如何通过requests发送GET和POST请求。这一篇我们重点深入pytest框架的几个核心进阶能力fixture的scope与conftest设计、参数化数据驱动、断言技巧与失败定位、pytest.ini配置与常用插件组合。这些都是从“能写用例”到“能写出可维护、可扩展的接口测试项目”之间必须跨过的坎。适合阅读这篇内容的读者我建议是已经用过pytest写过简单接口用例但觉得自己的用例代码重复度高、维护成本大、且不知道怎么组织大型接口测试项目的同学。如果你刚接触pytest可以先花一天时间把基础用法过一遍再回来看这篇效果会好很多。2. fixture的scope与conftest设计——测试固件的复用之道2.1 什么是fixture为什么接口测试离不开它fixture是pytest最核心、也最容易被忽视价值的特性。简单理解fixture就是一个前置条件处理器在测试用例执行之前先帮你准备好测试所需的环境、数据、对象测试结束后再帮你做清理。举个例子你测一个“用户下单”的接口前提条件必须是用户已经登录。如果没有fixture你要在每一个测试用例里重复写登录逻辑def test_create_order(): # 每一步都要重新登录 login_resp requests.post(https://api.example.com/login, json{username: testuser, password: 123456}) token login_resp.json()[token] headers {Authorization: fBearer {token}} order_resp requests.post(https://api.example.com/orders, headersheaders, json{product_id: 1001, quantity: 2}) assert order_resp.status_code 200这还只是一个用例。如果订单模块有20个用例你就要复制20遍登录逻辑。这时如果登录接口的路径改了、参数变了、返回的字段名改了就意味着20处代码全部要改。这还不是最可怕的最可怕的是你复制时漏改了一处测试结果就会出现诡异的失败排查半天还以为是业务逻辑出了问题。用fixture改造之后代码会清爽很多import pytest import requests pytest.fixture def auth_token(): 登录并返回token login_resp requests.post(https://api.example.com/login, json{username: testuser, password: 123456}) assert login_resp.status_code 200 return login_resp.json()[token] def test_create_order(auth_token): headers {Authorization: fBearer {auth_token}} order_resp requests.post(https://api.example.com/orders, headersheaders, json{product_id: 1001, quantity: 2}) assert order_resp.status_code 200这样登录逻辑只写一遍所有用例通过参数名声明依赖即可。登录接口有变动只需要改fixture这一处。2.2 fixture的scope机制——function、class、module、session怎么选fixture有一个scope参数控制它的作用域和生命周期。这个参数选错了要么测试速度慢得让人抓狂要么用例之间互相污染导致断言莫名其妙地失败。pytest的scope一共有五个级别按生命周期从短到长排列级别生命周期适用场景function每个测试用例执行前都调用一次需要隔离的数据准备每个用例独立环境class每个测试类执行前调用一次同一个类下的用例共享前置条件module每个测试模块.py文件执行前调用一次同一模块内的用例共享登录态、连接等session整个测试会话只调用一次全局只执行一次的资源如数据库连接、全局配置package每个测试包执行前调用一次适用于分层目录结构下的包级共享这里我的建议非常明确接口自动化测试中90%的场景用function和session两种就足够了。为什么呢接口测试和数据准备之间天然存在依赖关系。比如注册接口的测试每一条用例都在创建新用户如果数据不隔离前一条用例创建的手机号可能被后一条用例再次使用结果注册接口返回“用户已存在”用例直接失败。这种场景必须用function级别的fixture保证每条用例启动前都重新准备数据。反过来登录token、数据库连接、Redis连接这类资源如果每条用例都重新获取一次几百条用例跑下来光是登录请求就发了上百次耗时翻倍。这种场景用session级别的fixture只登录一次然后所有用例共用同一个token。我自己踩过一个坑有一次为了省事把登录token定义成了session级别的fixture跑全量回归时发现部分用例偶发失败。排查了半天发现是因为token有效期只有30分钟全量用例跑了一个多小时后面的用例用的还是30分钟前获取的旧token自然全部401。后来我把自动刷新逻辑加到了fixture里每次调用前判断token剩余有效时间不足5分钟就重新登录。这个做法我用到现在非常稳。2.3 conftest.py应该怎么组织——层级、可见性、职责划分conftest.py是pytest里存放fixture的专用文件它的最大特色是不需要importpytest会自动识别而且有层级可见性。什么叫层级可见性就是说放在根目录conftest.py里的fixture对整个测试项目可见放在某个子目录里的conftest.py只对该目录及其子目录下的用例可见。实际项目中我推荐至少建两层conftest第一层是项目根目录的conftest.py专门放全局fixture比如读取配置文件的fixture、发送请求的封装fixture、全局的session级token获取fixture。project_root/ ├── conftest.py # 全局fixture ├── config/ │ ├── config.yaml │ └── env_config.py ├── tests/ │ ├── test_user.py │ ├── test_order.py │ └── test_payment/ │ ├── conftest.py # 支付模块专属fixture │ └── test_refund.py第二层是放在具体业务模块目录下的conftest.py只放该模块专属的fixture。比如支付模块测试目录下的conftest可以放“创建支付单”的fixture退款测试用得上但用户模块的测试不需要感知它的存在。这样设计的好处非常明显fixture的可见范围被严格限制不会出现全局fixture爆炸的局面。我见过有些团队把几百个fixture全塞进根目录的conftest.py里文件长达一千多行每次查看依赖关系都像考古。合理的做法是根目录conftest只放十几二十个真正全局通用的fixture业务相关的全部下沉到模块目录。2.4 fixture内建钩子yield实现后置清理fixture里有两段逻辑前置准备和后置清理。pytest通过yield关键字来区分这两段。yield之前的代码在用例执行前运行yield之后的代码在用例执行后运行。这个能力在接口测试里最典型的应用场景是测试数据清理pytest.fixture def create_user_and_clean(): 创建一个测试用户用例结束后删除该用户 # 前置创建用户 user_data {username: test_user_001, phone: 13800001234} resp requests.post(https://api.example.com/users, jsonuser_data) user_id resp.json()[user_id] yield user_id # 这里把user_id传给测试用例 # 后置删除该用户避免脏数据累积 requests.delete(fhttps://api.example.com/users/{user_id})这样每条用例跑完创建的用户都会被自动删除不会给线上环境留一堆垃圾数据。几个使用细节值得注意如果前置准备阶段就断言失败了yield后面的清理代码不会执行这是符合预期的因为根本还没创建出需要清理的资源。一个fixture可以yield多个值用return的写法也可以但return就不支持后置清理了。pytest里fixture用yield是标准写法。清理阶段如果抛异常pytest会标记用例失败所以要谨慎处理清理阶段可能出现的异常比如请求超时建议加个try...except包裹住清理代码。3. 参数化——数据驱动测试的核心实现方式3.1pytest.mark.parametrize基础用法接口测试的灵魂是数据驱动同一个接口用不同的入参组合验证不同的预期结果。如果每个参数组合都单独写一个用例函数代码会冗余到无法维护。pytest的parametrize装饰器就是专门解决这个问题的。看一个实际例子测试登录接口的正常流程import pytest import requests pytest.mark.parametrize(username,password,expected_code, [ (testuser, 123456, 200), (admin, admin123, 200), (testuser, wrongpass, 401), ]) def test_login(username, password, expected_code): resp requests.post( https://api.example.com/login, json{username: username, password: password} ) assert resp.status_code expected_code运行时pytest会自动生成三个测试用例用例ID默认是参数值的组合。这样写一个函数就能覆盖多组数据。新增一组测试数据只需要在列表里加一个元组用例数量自动扩展维护成本极低。关于parametrize有几个细节容易被忽略参数名和函数入参名必须一一对应漏一个参数名就会报错。期望值也建议放在参数列表里而不是在断言里写死。比如上面例子把expected_code作为参数不同数据组可以断言不同的状态码。如果参数特别多可以给每一组数据加一个id这样测试报告中能看到有意义的用例名称。比如pytest.mark.parametrize(username,password,expected_code, [ (testuser, 123456, 200), (testuser, wrongpass, 401), ], ids[correct_password, wrong_password])加了id之后测试报告中显示的不再是元组的原始值而是你自定义的语义化名称一眼就能看出这条用例测的是什么场景。3.2 从外部文件读取测试数据——Excel、JSON、YAML写死在代码里的参数化数据只适合小规模场景。真实的接口测试项目中测试数据通常独立于代码存在方便测试人员甚至产品、运营直接维护。我从实践中比较推荐的方案是YAML文件存数据 pytest_generate_tests或parametrize动态加载。用pytest_generate_tests钩子动态生成参数化用例# conftest.py import pytest import yaml def pytest_generate_tests(metafunc): if login_data in metafunc.fixturenames: with open(data/login_data.yaml, r, encodingutf-8) as f: data yaml.safe_load(f) metafunc.parametrize(login_data, data)# data/login_data.yaml - case_name: 正确账号密码 username: testuser password: 123456 expected_code: 200 expected_msg: success - case_name: 密码错误 username: testuser password: wrongpass expected_code: 401 expected_msg: invalid password# test_login.py def test_login_with_data(login_data): resp requests.post( https://api.example.com/login, json{username: login_data[username], password: login_data[password]} ) assert resp.status_code login_data[expected_code]YAML方案的核心优势是测试数据和用例逻辑完全分离针对接口字段的边界值、异常值测试只需要更新YAML文件不需要改动任何代码。我负责的一个数据中台项目接口字段有几十个每个字段的边界值、非法值、超长值加起来上百条用例全靠YAML数据驱动来维护否则代码量会爆炸到不可收拾。有一点要特别提醒YAML文件对缩进非常敏感数据文件如果有中文建议UTF-8编码并且在加载时指定encodingutf-8避免Windows环境下出现乱码问题。3.3 参数化遇到fixture时的组合技巧接口测试中经常出现一个场景参数化的每一组数据都需要依赖同一个fixture资源。比如测试创建订单接口每条用例不仅需要不同的商品数据还需要一个已经登录的token。这两种能力如何组合直接作为函数参数混写即可pytest.fixture def auth_token(): return fake_token_123 pytest.mark.parametrize(product_id,quantity,expected_code, [ (1001, 2, 200), (1002, 0, 400), (9999, 1, 404), ]) def test_create_order(auth_token, product_id, quantity, expected_code): headers {Authorization: fBearer {auth_token}} resp requests.post( https://api.example.com/orders, headersheaders, json{product_id: product_id, quantity: quantity} ) assert resp.status_code expected_code这里每个参数组合执行前pytest都会先调用auth_token这个fixture。如果auth_token是function级别的那每条用例都会重新执行登录如果是session级别的那整个测试过程只登录一次各个参数组合共享token。还有一个容易踩坑的点如果希望先用fixture处理数据再传给测试用例可以写一个“fixture工厂”或者把参数化逻辑封装在fixture内部。pytest.fixture(params[ (1001, 2, 200), (1002, 0, 400), (9999, 1, 404), ]) def order_case_data(request): product_id, quantity, expected_code request.param return { product_id: product_id, quantity: quantity, expected_code: expected_code, }这种写法的好处是把数据和fixture绑定在一起测试函数内只声明对order_case_data的依赖整体逻辑更内聚。4. 接口响应断言进阶——断言不是简单的“等于”4.1 多个断言层级的设计思路很多人在接口测试中断言只停留在status_code 200这个层面。这个做法对冒烟测试够用但对专业级的接口自动化远远不够。一个接口的响应体通常包含多层信息HTTP状态码、响应头、业务状态码、消息体、数据字段。我的经验是至少做三层断言第一层HTTP状态码。这是最基本的风向标判断请求是否到达服务器并被成功处理。第二层业务状态码。很多接口即使HTTP返回200业务上可能返回错误码比如“用户不存在”“库存不足”。所以必须同时断言业务状态码resp_body resp.json() assert resp_body[code] 0 assert resp_body[msg] success第三层关键业务字段的取值。这是最精细的校验用于验证接口返回的核心数据是否符合预期。比如登录接口返回的token字段是否有值、用户详情接口返回的nickname是否和传入参数一致。assert resp_body[data][token] is not None assert len(resp_body[data][token]) 20三层断言结合起来才算真正验证了接口的业务逻辑正确性。只断言HTTP 200有一个严重缺陷某些接口在业务异常时依然返回HTTP 200但response body里携带着业务错误码。这种情况一旦漏检问题会被悄悄吞掉。4.2 断言失败时的可读性优化pytest原生的assert已经很好用了但有时候断言失败信息不够直观。比如assert resp_body[data][user][age] 18当age返回19而不是18时pytest只会告诉你assert 18 19你根本无法从这段信息里判断是哪个接口、哪个字段出了问题——尤其当几百条用例同时跑失败时找起原因来非常痛苦。我的做法是断言加注释或自定义消息assert resp_body[data][user][age] 18, 用户年龄字段断言失败实际返回: {}.format(resp_body[data][user][age])这样测试报告里就能直接看到“用户年龄字段断言失败实际返回: 19”定位问题的时间能省一半。对于嵌套复杂的响应体推荐写一个通用断言工具函数def assert_json_field(resp_body, json_path, expected_value): 支持用点号路径断言嵌套字段 current resp_body for part in json_path.split(.): assert part in current, fJSON路径 {json_path} 中未找到字段 {part} current current[part] assert current expected_value, f字段 {json_path} 期望 {expected_value}实际 {current}然后调用assert_json_field(resp_body, data.user.age, 18)失败时的提示信息就非常清楚了。我用这个工具函数几乎跑遍了所有项目的接口断言非常顺手。4.3 断言时机的选择——轮询断言与超时控制有一类接口比较特殊接口本身只负责提交任务任务在后台异步执行。比如导出报表接口、批量处理接口。这类接口提交后立刻返回“处理中”真正的结果要隔几秒或几十秒才能查到。如果按同步接口的思路去断言基本必挂。针对这种场景我的方案是封装一个轮询断言函数import time def assert_with_retry(check_func, expected, timeout30, interval2): 轮询断言每隔interval秒执行一次check_func直到超时或断言成功 :param check_func: 返回需要断言的值 :param expected: 期望值 :param timeout: 超时时间秒 :param interval: 轮询间隔秒 start_time time.time() while time.time() - start_time timeout: actual check_func() if actual expected: return True time.sleep(interval) assert actual expected, f轮询超时{timeout}s最后一次结果为 {actual}期望为 {expected}调用方式def test_export_report(): # 提交导出任务 resp requests.post(https://api.example.com/export, json{report_type: order}) task_id resp.json()[data][task_id] # 轮询查询任务状态直到成功或超时 assert_with_retry( check_funclambda: requests.get(fhttps://api.example.com/export/status/{task_id}).json()[data][status], expectedsuccess, timeout60, interval3 )这里边有一个细节轮询间隔不建议设得太短比如0.5秒否则对服务器压力太大。2到3秒是灰常合理的默认值。超时时间根据接口的实际处理耗时来定宁可设长一点也不要因为偶发超时而让用例误报失败。5. pytest配置与常用插件组合——把测试产物做扎实5.1 pytest.ini的常用配置项pytest.ini是pytest的配置文件放在项目根目录很多能用命令行参数实现的功能都可以固化到这个文件里保证所有团队成员跑测试时的行为一致。我的一个标准pytest.ini配置长这样[pytest] minversion 6.0 testpaths tests python_files test_*.py python_classes Test* python_functions test_* addopts -v -s --tbshort --strict-markers markers smoke: 冒烟测试用例 regression: 回归测试用例 slow: 慢速用例 log_cli true几个比较关键的配置项testpaths指定测试用例的目录。pytest执行时不用再传路径直接跑pytest即可避免误收集到项目里其他目录下名为test_开头的脚本。addopts将命令行常用参数固化到配置文件中。-v显示详细用例信息-s让print输出可见--tbshort控制失败时的堆栈长度--strict-markers强制要求使用的marker必须先声明。markers提前注册marker。声明在pytest.ini里可以在测试报告中区分用例等级也可以配合命令行参数只执行指定标记的用例。比如冒烟测试只跑接口核心链路。5.2 常用插件组合pytest-html、pytest-ordering、allurepytest强大的插件生态是它比unittest更受欢迎的重要原因之一。我这里推荐三个实际项目中最常用的插件组合。第一个是pytest-html生成简单的HTML测试报告。安装方式pip install pytest-html执行时指定报告输出路径pytest --htmlreport.html --self-contained-html--self-contained-html参数会把CSS样式内嵌到HTML文件中方便直接通过浏览器打开或作为附件发给别人。这个插件的优点是用起来零成本缺点是报告样式比较朴素信息也不够细致。第二个是pytest-ordering控制用例执行顺序。有些团队会对用例执行顺序有要求虽然我不推荐测试用例之间有强依赖但在某些场景下比如必须先创建数据再执行查询顺序就是必要的。插件支持pytest.mark.run(order2) def test_b(): pass pytest.mark.run(order1) def test_a(): pass第三个是allure-pytest这是目前接口测试报告方案的天花板。生成出来的报告包含测试步骤、请求响应信息、失败截图、历史趋势等非常专业。pip install allure-pytest执行时生成结果文件然后用allure命令生成报告pytest --alluredir./allure-results allure serve ./allure-results我个人的建议是团队刚起步时用pytest-html就够轻量、快速。要对外汇报或者做持续集成时再上allure它会让整个测试产物的专业度提升一个档次。5.3 通过marker实现场景分级与定向执行marker是pytest的另一个重要特性在接口测试中的重要应用是把用例分级、分场景然后按需执行。应用场景非常典型一个项目有300条接口用例每次代码提交后全量回归要跑40分钟。但如果只改了登录模块的代码跑全量的性价比就很低。我的做法是给关键核心链路、最常用接口的用例打上smoke标记每次变更后先跑冒烟集冒烟全绿再跑全量。标记方式pytest.mark.smoke def test_login_success(): pass pytest.mark.regression def test_query_user_detail(): pass执行指定标记的用例pytest -m smoke还可以组合多个标记pytest -m smoke and not slow这种场景分级的方式能让测试反馈速度大幅提升。在CI流水线里我的建议是合并构建先跑smoke如果冒烟集是通的再启动全量回归最后用allure把两种情况的结果分别展示。5.4 配置文件与全局请求封装——让测试代码更干净接口测试中环境地址开发环境、测试环境、预发布环境经常频繁切换。如果每个用例里都硬编码域名切换环境时就会非常痛苦。我的做法是增加一个全局配置加载fixture和三套环境配置# config/env_config.py ENV_CONFIG { dev: { base_url: https://dev.api.example.com, timeout: 5 }, test: { base_url: https://test.api.example.com, timeout: 10 }, pre: { base_url: https://pre.api.example.com, timeout: 10 } }全局fixture# conftest.py import pytest from config.env_config import ENV_CONFIG pytest.fixture(scopesession) def base_url(): 通过环境变量指定当前环境默认test环境 env os.getenv(TEST_ENV, test) return ENV_CONFIG[env][base_url]执行时指定环境TEST_ENVtest pytest # 使用测试环境 TEST_ENVpre pytest # 使用预发布环境这套方案我已经在多个项目里使用切换环境只需改一个环境变量用例代码完全不需要改动。对于支付这类需要测试不同环境的接口测试项目这个能力能节省大量的维护时间。6. 常见问题与排查技巧实录6.1 用例执行顺序混乱现象接口用例的执行顺序和你写代码的顺序不一致导致某些用例依赖的前置用例还没跑完就失败了。原因pytest默认按照文件内定义顺序收集但多个文件之间遵循的是文件名字母顺序。如果你有test_b.py和test_a.pypytest会先执行test_a.py。解决最简单的方式梳理用例依赖尽量消除跨文件的顺序依赖。用pytest-ordering插件对确实有依赖关系的用例指定顺序执行。我个人的建议是尽量不依赖顺序。接口测试用例之间应该是独立的每条用例都自带前置数据准备而不是依赖上一条用例的执行结果。这是一个从入门到进阶必须建立的意识。6.2 fixture中发送请求超时、失败导致大量用例扩散失败现象某个session级别的fixture挂了比如登录接口超时、数据库连接失败导致依赖它的所有用例全部报error测试报告一片红。原因session级别的fixture一旦执行失败pytest会跳过所有依赖该fixture的用例报错信息是fixture调用异常。解决排查fixture本身的问题比如网络超时、服务异常修复后重新执行。优化fixture的健壮性加上重试机制。import time pytest.fixture(scopesession) def auth_token(): for attempt in range(3): try: resp requests.post(https://api.example.com/login, timeout5) if resp.status_code 200: return resp.json()[token] except requests.exceptions.RequestException: pass time.sleep(2) raise RuntimeError(登录接口连续3次调用失败无法获取token)重试三次再失败大概率就是服务真的有问题了fixture这时报错是合理的不用过度包装。6.3 YAML测试数据里中文乱码、特殊字符解析报错现象yaml.safe_load读取中文字段时正常但断言失败信息里中文显示为乱码或者数据中包含特殊字符如#、冒号导致yaml解析直接报错。原因YAML语法里#开头是注释冒号后要加空格才会被识别为键值分隔。比如expected_msg: 下单成功#优惠中#前面的双引号其实就是字符串但如果没加引号YAML解析器会把#优惠部分当注释处理数据直接缺了一块。解决所有包含特殊字符的值必须加双引号包裹。读取yaml文件时指定UTF-8编码with open(path, r, encodingutf-8) as f: data yaml.safe_load(f)输出到控制台时如果还是乱码检查pytest的输出编码设置Windows下建议设置环境变量PYTHONIOENCODINGutf-8。6.4 断言失败但无法定位具体请求参数现象断言失败时测试报告只展示了失败断言的信息但调用接口时发送的请求参数、请求头完全看不到排查问题要重新加日志跑一遍。解决在发送请求的封装函数里统一记录请求参数和响应内容。我习惯封装一个api_request模块def api_request(method, url, **kwargs): full_url BASE_URL url print(f请求接口: {method.upper()} {full_url}) print(f请求参数: {json.dumps(kwargs.get(json) or kwargs.get(params), ensure_asciiFalse)}) resp requests.request(method, url, **kwargs) print(f响应状态: {resp.status_code}) print(f响应内容: {resp.text[:500]}) return resp配合pytest的-s参数可以把这些日志实时输出到控制台。如果用了allure还可以通过allure.attach把这些信息附加到测试步骤中import allure allure.attach(json.dumps(payload, ensure_asciiFalse), name请求参数, attachment_typeallure.attachment_type.JSON) allure.attach(resp.text, name响应结果, attachment_typeallure.attachment_type.TEXT)这个习惯一旦养成排错效率至少翻倍。6.5 测试报告为空的常见原因现象跑完pytest后生成的HTML或allure报告里用例数量为0。原因收集用例时路径指向不对pytest没有匹配到任何test_*.py文件。pytest.ini里的testpaths路径不正确。用例文件名、函数名没有遵循默认的test_前缀规则。解决先运行pytest --collect-only看pytest到底收集到了哪些用例。如果显示no tests ran大概率就是收集路径问题检查testpaths和文件名命名即可。写在最后的几点经验做接口自动化测试这几年最大的体会是框架本身不难学难的是把一个测试项目组织得干净、健壮、可维护。pytest给我们的fixture、参数化、插件体系都是很好的抓手但真正拉开差距的是使用方式。说两个我个人的习惯。第一是测试数据尽量外部化不同环境的差异用配置文件解决不要让环境信息散落在用例代码里。第二是断言信息一定要有上下文别只写一个裸的assert否则将来报告出来根本没法看。做到这两点不管项目规模多大复盘自动化测试时都不会太头疼。接口测试这条路基本功打得越扎实后面做性能测试、做测试平台时就越轻松。pytest这个框架能玩的东西还很多下一篇我会继续分享一些更深入的实践经验。

相关新闻

Jmeter接口测试与性能测试实战:从脚本到压测报告

Jmeter接口测试与性能测试实战:从脚本到压测报告

做了七八年的接口测试和性能测试,Jmeter是我电脑里装了最久、卸载次数最少的一个工具。早些年是配合自动化框架做接口回归,后来被拉去支撑性能测试,发现团队里会写脚本的多,能把线程数、Ramp-Up、断言、关联这些参数搭配明白的少。…

2026/10/18 16:52:46 阅读更多 →
UI自动化维护难?POM页面对象模型实战指南

UI自动化维护难?POM页面对象模型实战指南

做了几年UI自动化的人,大概率都经历过这个阶段:脚本越写越多,维护成本越来越高,每次前端改版都像抽签,不知道哪些用例会挂。我之前带过一个项目,登录按钮的ID从login_btn改成了submit_btn,全仓库…

2026/10/18 16:52:46 阅读更多 →
桥梁传感器数据集实战:从数据预处理到损伤检测全流程解析

桥梁传感器数据集实战:从数据预处理到损伤检测全流程解析

简介:这份代码包围绕大型跨江桥梁结构健康监测场景,基于2023年10月1日8:00至12:45的连续实测数据构建,整合主跨跨中、桥塔基座、伸缩缝区域、悬索锚固点及桥面铺装层5个关键部位传感器(S1-S5)的振动、温度与应力参数&a…

2026/10/18 16:51:46 阅读更多 →

最新新闻

2B参数视觉语言模型Youtu-Parsing:复杂文档解析新范式,替代传统OCR管道

2B参数视觉语言模型Youtu-Parsing:复杂文档解析新范式,替代传统OCR管道

文档解析这个赛道,这几年肉眼可见地热闹起来了。前几年大家还在用“OCR文字识别手工规则”硬啃版面,现在直接上视觉语言模型(VLM)来做端到端的文档解析,效果确实是一代一代在进步。最近我拿到了一套名为Youtu-Parsing的…

2026/10/18 17:45:45 阅读更多 →
MoE原理与负载均衡:稀疏大模型训练、推理成本优化实践

MoE原理与负载均衡:稀疏大模型训练、推理成本优化实践

说实话,第一次听说某个模型用上“混合专家”(MoE)时,我的第一反应是:这不就是集成学习换了个名字吗?多个专家凑在一起投票,怎么可能反而省算力?后来把相关论文和实现逐行看完&#x…

2026/10/18 17:45:45 阅读更多 →
SSM分诊系统:基于状态机的医疗资源调度实战

SSM分诊系统:基于状态机的医疗资源调度实战

简介:本资源是一套基于SSM框架(SpringSpringMVCMyBatis)开发的医院分诊管理信息系统完整源码,面向Java初学者与Web项目实践者,聚焦医疗场景下的分诊叫号、患者信息动态管理及科室配置等核心业务需求。压缩包含2000个文…

2026/10/18 17:45:45 阅读更多 →
约瑟夫环C语言实现:循环链表核心算法与调试指南

约瑟夫环C语言实现:循环链表核心算法与调试指南

约瑟夫环这道题,写过的同学都知道,说穿了就是循环链表加两个指针来回挪,但当初我为了把它调通,愣是在宿舍debug到凌晨。后来把“前驱指针”的思路理顺了,再回头看,其实这个题目考的就两件事:把环…

2026/10/18 17:45:45 阅读更多 →
AI日报:大模型推理成本下探与Agent长任务、量化部署实战观察

AI日报:大模型推理成本下探与Agent长任务、量化部署实战观察

今天是2026年10月11日,星期日。按理说周末不该谈工作,但AI这个圈子似乎从来没有“打烊”的概念——模型权重照样发新版本,开源仓库照样有人半夜提PR,评测集照样在刷新记录。我趁着下午把近期圈里值得看的东西捋了一遍,…

2026/10/18 17:45:45 阅读更多 →
降AI万能公式是伪命题?从AI检测原理到可复用改写框架的完整指南

降AI万能公式是伪命题?从AI检测原理到可复用改写框架的完整指南

“降AI万能公式”最近在内容创作圈子里热度很高,很多朋友私信问我:网上流传的那些“降AI提示词”“一键去AI味模板”到底有没有用?是不是真有那么一套万能公式,套进去就能让AI写的字变成“人写的”?我认真拆解过这些方…

2026/10/18 17:44:45 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →