尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python自动化测试实战:从pytest+Selenium到接口测试与持续集成
做了这么多年测试开发我越来越觉得“Python 自动化测试”这件事真正卡住大部分人的地方并不是代码本身而是不知道整套东西该怎么串起来。日常在社区里也经常看到新手提问学了 Selenium 语法、会写几个 pytest 用例可一放到真实项目里就到处碰壁。这次我就以一套完整的实战路径为主线从技术选型、环境搭建、UI 自动化、接口自动化、测试报告到问题排查把详细步骤和代码示例一次讲透让大家照着做就能搭出属于自己的 Python 自动化测试体系。这套内容适合刚入门自动化测试的测试人员也适合想把手动测试流程做一次升级的开发同学。无论你最后是想做 Web UI 自动化、接口自动化还是想往测试开发方向走下面这套思路和代码骨架都能直接复用。1. 项目概述与自动化测试的核心设计思路1.1 自动化测试到底解决了什么问题先想清楚一个问题我们为什么需要自动化测试很多人第一反应是“省时间”这个答案对了一半。真正做过自动化的人会告诉你自动化测试最大的价值不在于“跑得快”而在于“愿意反复跑”。手工测试的痛点在于回归成本太高一个版本迭代下来核心流程全部手点一遍可能要一两个小时痛点不是慢而是“你不可能每次都把 100 条用例全部点完”。人会在疲劳时漏掉关键步骤但脚本不会。所以自动化测试最适合的场景是那些“高频回归、核心链路、重复操作密集”的测试场景。比如登录注册流程、订单提交流程、支付回调流程这类业务一旦出错影响面极大又是每个版本都会涉及的功能非常适合优先做成自动化。而一些探索性的、视觉交互极强、需求频繁快速变化的功能我建议暂时不要强行自动化否则维护成本会远超收益。1.2 技术选型为什么是 pytest Selenium Requests市面上做自动化测试的语言和框架很多Java 体系有 TestNG、RestAssuredPython 体系有 unittest、pytest、Robot FrameworkNode 体系有 Playwright、Cypress。我为什么一直推荐 Python 这套组合核心原因有三个pytest 的断言机制非常友好直接用 Python 原生的assert语句即可不需要像 Java 里的 TestNG 那样记住一堆断言方法。fixture 机制强大比如登录操作只需要写一次多个用例可以自由复用这在接口自动化里能直接省掉大量重复代码。插件生态成熟失败重跑、生成 HTML 报告、多进程并发执行、参数化测试这些常见需求都有现成插件一两条命令就能搞定。UI 自动化选 Selenium 而不是 Playwright 和 Cypress主要是考虑到兼容性和资料丰富度。Selenium 是老牌框架支持 Chrome、Firefox、Edge 等主流浏览器网上遇到的问题基本都能搜到答案。做接口自动化则用 Requests它简洁直观配合 pytest 足以应对绝大多数企业级接口测试需求。1.3 自动化测试项目的目录结构规划很多人写自动化测试失败的原因之一是代码全部堆在一个文件里测试代码和业务逻辑搅在一起用例一多就没法维护。我比较推荐一开始就按下面的结构规划项目test_project/ ├── conftest.py # pytest 全局配置和 fixture ├── requirements.txt # 依赖清单 ├── test_cases/ │ ├── test_ui_login.py # UI 测试用例 │ ├── test_ui_order.py │ ├── test_api_user.py # 接口测试用例 │ └── test_api_order.py ├── data/ │ ├── login_cases.json # 登录测试数据 │ └── user_data.yaml ├── reports/ # 测试报告输出目录 ├── utils/ │ ├── driver_factory.py # 浏览器驱动封装 │ └── http_client.py # HTTP 请求封装 └── logs/这个结构好在哪测试用例、测试数据、底层封装、报告输出完全分离。用例文件只关心业务场景和断言不关心浏览器怎么起、请求怎么发。一旦项目变大可以继续拆出pages/目录做 Page Object 模式封装把页面元素定位和测试逻辑彻底解耦。后面所有代码示例我都会基于这种分层思想来写大家可以直接照抄改造。2. 环境准备与依赖安装的详细步骤2.1 Python 虚拟环境配置在开始写自动化代码之前环境问题是很多新手要过的第一关。我的建议是不管你是 Windows、macOS 还是 Linux一定不要把依赖直接装到系统 Python 里否则不同项目之间的依赖版本会互相污染解决起来非常痛苦。推荐用虚拟环境Python 3.3 以上自带的venv就完全够用。Windows 下的操作步骤大致是# 创建虚拟环境会在当前目录生成 venv 文件夹 python -m venv venv # 激活虚拟环境Windows venv\Scripts\activate # 激活虚拟环境macOS / Linux source venv/bin/activate激活之后命令行前缀会出现(venv)标识说明你已经身处虚拟环境中了。如果你更习惯用 Anaconda也可以用conda create -n test_env python3.11创建专用环境本质是同一个思路。我个人在团队里更喜欢原生venv因为它更轻量、不依赖额外软件。2.2 自动化测试依赖库安装环境激活之后直接通过 pip 安装依赖即可。先把核心依赖列一下pip install pytest selenium requests pytest-html pytest-rerunfailures webdriver-manager这里每个库的作用我都简单说明一下pytest测试框架负责收集和执行测试用例。seleniumWeb UI 自动化核心库调用浏览器执行操作。requests接口调用核心库模拟 HTTP 请求。pytest-html生成 HTML 格式的测试报告。pytest-rerunfailures失败用例自动重跑提升稳定性。webdriver-manager自动下载和管理浏览器驱动解决 Chrome 版本升级后 driver 不匹配的问题。前端开发的同学如果用 VSCode记得在项目根目录建一个.vscode/settings.json把 Python 解释器指向刚才创建的虚拟环境路径这样写代码时就能正确识别依赖包的代码提示了{ python.defaultInterpreterPath: ./venv/bin/python }装完之后可以把依赖冻结到requirements.txt里方便团队其他成员一键复现环境pip freeze requirements.txt2.3 浏览器驱动管理技巧做 Selenium 自动化测试驱动版本不匹配是一个高频问题。Chrome 经常自动升级导致 chromedriver 和浏览器版本对不上脚本报SessionNotCreatedException。最优雅的解法是使用webdriver-manager它会在第一次运行的时候根据你本机 Chrome 的实际版本自动下载对应的驱动之后每次启动时判断版本是否需要更新。相比手动去官网下载驱动文件再扔到 PATH 里这个方案省心太多。3. UI 自动化测试实战Selenium 完整操作流程3.1 浏览器对象封装与生命周期管理UI 自动化的基础是先能稳定地启动一个浏览器。我建议把 driver 的创建、初始化和关闭封装到 pytest 的 fixture 里而不是在每个用例里重复写webdriver.Chrome()。在项目根目录的conftest.py中定义如下 fixtureimport pytest from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager pytest.fixture() def driver(): 启动 Chrome 浏览器测试结束后自动关闭 service Service(ChromeDriverManager().install()) option webdriver.ChromeOptions() # 窗口最大化减少元素被遮挡的概率 option.add_argument(--start-maximized) # 禁用浏览器弹窗 option.add_experimental_option(detach, False) browser webdriver.Chrome(serviceservice, optionsoption) browser.implicitly_wait(5) yield browser browser.quit()这里的yield是 fixture 的核心用法yield之前是前置操作启动浏览器之后是后置操作关闭浏览器。用例执行完不管断言成功还是失败browser.quit()都会执行不会留下大量残留的浏览器进程。3.2 第一个完整的登录测试用例一切环境就绪我们来写第一个真正能跑的 UI 自动化测试用例。以登录场景为例完整代码如下import pytest from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestLogin: def test_login_success(self, driver): 正确账号密码登录断言跳转到首页 driver.get(https://example.com/login) # 等待用户名输入框出现 username_input WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, username)) ) username_input.send_keys(tester01) driver.find_element(By.ID, password).send_keys(123456) driver.find_element(By.ID, btn_login).click() # 断言登录后 URL 是否包含 /home WebDriverWait(driver, 10).until( EC.url_contains(/home) ) assert /home in driver.current_url代码逻辑并不复杂但背后有几个新手经常踩的坑需要注意。第一个坑是用time.sleep(5)这种固定等待代替显式等待这会拖慢执行速度而且网络慢的时候照样会找不到元素。第二个坑是把所有定位方式都写成find_element(By.XPATH, ...)一旦前端稍作调整用例立刻挂掉。我后面会单独讲定位策略的取舍。3.3 元素定位策略优先级与等待机制关于元素定位我在实际项目中总结了一套优先级顺序有 id 用 idid 在 HTML 规范里理论上唯一定位最快最稳。没有 id 用 namename 属性是表单元素最常见的标识。name 也没有就用 CSS SelectorCSS 定位速度快语法简洁。最后才考虑 XPathXPath 灵活但绝对路径式的写法会非常脆弱。如果前端的 id 都是动态生成的比如每次刷新页面 id 都会变那就不要硬用 id直接用 CSS 配合其他稳定属性更靠谱。比如# 用 class 属性定位 driver.find_element(By.CSS_SELECTOR, button.btn-login) # 用>import pytest from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestLoginParams: pytest.mark.parametrize(username,password,expected_url, [ (tester01, 123456, /home), (tester01, wrong, /error), (, , /login), ], ids[success, wrong_password, empty_account]) def test_login(self, driver, username, password, expected_url): driver.get(https://example.com/login) driver.find_element(By.ID, username).send_keys(username) driver.find_element(By.ID, password).send_keys(password) driver.find_element(By.ID, btn_login).click() WebDriverWait(driver, 10).until( EC.url_contains(expected_url) ) assert expected_url in driver.current_urlids参数是给每条用例起一个可读名字测试报告里会显示为test_login[success]排查失败时一眼就能看出来是哪组数据出的问题。3.5 Page Object 模式让 UI 用例告别重构灾难用例数量超过 20 条以后如果所有元素定位都散落在用例代码里一旦前端改版你得逐条用例去改定位表达式那画面想想都崩溃。行业里标准解法是 Page Object 模式简单说就是把一个页面的元素定位和操作方法封装成一个类用例只调用方法不接触定位细节。比如登录页面封装为LoginPagefrom selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: def __init__(self, driver): self.driver driver self.username_input (By.ID, username) self.password_input (By.ID, password) self.login_btn (By.ID, btn_login) def login(self, username, password): wait WebDriverWait(self.driver, 10) wait.until(EC.visibility_of_element_located(self.username_input)).send_keys(username) self.driver.find_element(*self.password_input).send_keys(password) self.driver.find_element(*self.login_btn).click()用例里只需要三行def test_login_success(self, driver): LoginPage(driver).login(tester01, 123456) assert /home in driver.current_url页面改版时你只需要去LoginPage里改定位表达式用例层完全不用动。虽然前期多写了一点封装代码但项目一进入维护期这套结构的优势会立刻体现出来。这是我带团队做 UI 自动化多年最想强调的一点自动化测试项目里代码结构的重要性远远大于用例数量。4. 接口自动化测试实战Requests 与 pytest 的强强联合4.1 接口自动化的基础请求与断言接口自动化测试相比 UI 自动化执行速度快一个量级稳定性也高得多所以如果项目还在早期我强烈建议优先从接口自动化做起。用 Requests 发一个 GET 请求并断言响应代码非常简洁import requests def test_get_user_info(): url https://api.example.com/users/1001 headers {Authorization: Bearer your_token} resp requests.get(url, headersheaders, timeout10) assert resp.status_code 200 body resp.json() assert body[code] 0 assert body[data][username] tester01这里我特意加了timeout10这是一个很多人容易忽略的参数。如果不设置超时当接口服务异常时请求可能挂起数分钟才报错整个测试套件的执行时间会被拖垮。设了超时之后接口无响应时能快速失败也方便后续做重试。断言响应结构时不建议用assert resp.text ...这种全量对比方式。响应体里一旦有动态字段比如时间戳、随机 ID用例就非常脆弱。比较稳妥的做法是分层断言先断言状态码再断言业务码code最后只对关键的字段值做精确校验。4.2 用 fixture 处理登录态与 Token 复用接口测试最常遇到的业务场景是很多接口需要登录后才能调用登录返回一个 Token后续所有请求都要带着这个 Token。如果每个用例都去调一次登录接口不仅速度慢而且账号频繁登录可能触发服务端的风控策略。合理的做法是用 pytest 的 session 级别 fixture在整个测试会话期间只登录一次Token 由所有用例共享import pytest import requests BASE_URL https://api.example.com pytest.fixture(scopesession) def session(): 创建统一的 HTTP 会话并完成登录获取 Token http requests.Session() login_data {username: tester01, password: 123456} resp http.post(f{BASE_URL}/login, jsonlogin_data, timeout10) assert resp.status_code 200 token resp.json()[data][token] # 把 token 注入到会话的默认请求头中 http.headers.update({Authorization: fBearer {token}}) return http def test_get_user_info(session): resp session.get(f{BASE_URL}/users/1001, timeout10) assert resp.status_code 200 assert resp.json()[data][username] tester01 def test_get_user_orders(session): resp session.get(f{BASE_URL}/users/1001/orders, timeout10) assert resp.status_code 200 assert isinstance(resp.json()[data][list], list)使用requests.Session()还有一个好处它会自动保持 cookies多个请求之间可以共享会话状态避免每次请求都要手动处理这些上下文信息。4.3 接口测试数据驱动从 JSON 文件读取用例接口测试的用例数量往往很多把测试数据全部硬编码在代码里会让代码变得臃肿。我习惯把测试数据和业务参数单独放到 JSON 或 YAML 文件里代码只负责读取和执行。以data/login_cases.json为例[ { case_name: 正确账号密码登录, payload: {username: tester01, password: 123456}, expected_status: 200, expected_code: 0 }, { case_name: 密码错误, payload: {username: tester01, password: wrong}, expected_status: 200, expected_code: 1001 }, { case_name: 账号不存在, payload: {username: ghost_user, password: 123456}, expected_status: 200, expected_code: 1002 } ]测试代码通过json.load读取数据配合参数化执行import json import pytest import requests BASE_URL https://api.example.com def load_cases(): with open(data/login_cases.json, encodingutf-8) as f: return json.load(f) pytest.mark.parametrize(case, load_cases(), idslambda c: c[case_name]) def test_login(case): resp requests.post(f{BASE_URL}/login, jsoncase[payload], timeout10) assert resp.status_code case[expected_status] assert resp.json()[code] case[expected_code]这样以后要加测试数据直接往 JSON 文件里加一条记录就行不需要动代码。而且测试报告里每一条数据都会显示对应的case_name对账非常方便。4.4 接口依赖处理上一个接口的返回作为下一个接口的入参实际业务里接口之间常见依赖关系。比如下单接口需要传入刚刚创建出来的订单 ID支付接口需要传入订单 ID 和支付金额。这种场景下如果把用例写成相互调用会形成用例间的强耦合非常不利于单独运行和排查。我的做法是把“准备数据”的动作放到 fixture 里用例只关注自己的业务断言。比如支付接口依赖已存在订单就在 fixture 里先创建订单pytest.fixture() def created_order(session): 创建一笔订单返回订单 ID resp session.post(f{BASE_URL}/orders, json{product_id: 888, quantity: 1}, timeout10) assert resp.status_code 200 return resp.json()[data][order_id] def test_pay_order(session, created_order): pay_data {order_id: created_order, pay_method: alipay} resp session.post(f{BASE_URL}/orders/pay, jsonpay_data, timeout10) assert resp.status_code 200 assert resp.json()[data][status] paid如果created_order创建失败pytest 会直接标记支付用例失败但根本原因是数据准备失败而不是支付逻辑有问题。我们可以在 fixture 里加上清晰的断言这样排查起来速度会快很多。5. 测试报告生成、失败重试与持续集成5.1 使用 pytest-html 生成可视化测试报告测试跑完总要给团队看结果pytest-html 是最快出效果的方式。执行如下命令会在reports目录下生成一个独立的 HTML 报告pytest test_cases/ --htmlreports/report.html --self-contained-html--self-contained-html参数很关键它会把 CSS、JS 全部内嵌到 HTML 文件里这样直接把报告文件发给别人也能正常打开不用连带一堆资源文件。如果不加任何参数pytest 默认只收集test_*.py或*_test.py文件。你也可以在pytest.ini里做更细致的配置[pytest] testpaths test_cases python_files test_*.py python_classes Test* python_functions test_*这样执行命令时可以少敲很多参数。5.2 失败用例自动重试与并发加速自动化测试里最让人头疼的问题就是“偶发失败”——用例本身没问题但因为网络抖动、页面加载速度慢等原因偶尔挂一次。处理这种问题的标准姿势不是改用例而是用pytest-rerunfailures做自动重试pytest test_cases/ --reruns 2 --reruns-delay 1意思是如果用例失败等待 1 秒后重跑最多重试 2 次。如果重试后通过最终结果会被标记为通过报告中会注明重试次数。这个机制能显著提升测试套件的整体通过率。当用例规模上升到几百条时串行执行速度就会变慢。可以用pytest-xdist做多进程并发pytest test_cases/ -n 4-n 4表示同时开 4 个进程去跑用例。不过要提醒一点如果测试用例之间存在共享数据或共享浏览器窗口并发可能导致数据相互干扰。使用之前一定要保证用例的独立性否则并发会引出更多新问题。5.3 失败自动截图保存UI 自动化用例失败时如果只给一个“元素未找到”的报错信息开发同学很难定位问题。最有效的手段是失败时自动截图把现场保存下来。在conftest.py中加上这个 hook 即可import pytest pytest.hookimpl(tryfirstTrue, 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: driver.save_screenshot(freports/{item.name}_failed.png) print(f用例失败截图: reports/{item.name}_failed.png)这段代码是 pytest 的插件机制核心逻辑是在用例执行结束并拿到结果后判断当前阶段是否失败如果失败就获取 fixture 里的driver对象然后截图保存到 reports 目录。所谓的 hook 机制在 pytest 里很常见功能就是允许你在特定节点插入自己的逻辑理解成“用例执行完成后的系统回调”即可。5.4 接入持续集成的简单方案自动化测试的价值要靠持续执行来体现不然隔几周才手动跑一次意义会大打折扣。现在最轻量的方式是接入 GitHub Actions在仓库根目录创建.github/workflows/test.ymlname: Python Auto Test on: push: branches: [ main ] workflow_dispatch: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-pythonv5 with: python-version: 3.12 - run: pip install -r requirements.txt - run: pytest test_cases/ --htmlreports/report.html --self-contained-html - uses: actions/upload-artifactv4 if: always() with: name: test-report path: reports/这样每次代码 push 到主分支CI 会自动拉代码、装依赖、跑测试、上传测试报告。很多团队的自动化测试体系起步阶段用这一套方案已经能解决 80% 的问题了。6. 常见问题与排查技巧实录6.1 元素定位不到到底是谁的锅UI 自动化里最高频的错误就是NoSuchElementException。出现这个问题时不要急着改代码按下面顺序排查先看元素是否在 iframe 里如果页面里有嵌套的 iframe直接按普通方式定位会找不到需要先切进 iframedriver.switch_to.frame(frame_id)。再看元素是否在 Shadow DOM 里现在很多前端框架会把组件封装进 Shadow DOM常规定位同样找不到需要特殊处理。然后检查元素是否异步渲染如果元素是接口数据返回后才渲染出来的就一定要用WebDriverWait显式等待而不是implicitly_wait。最后看定位表达式是否唯一如果定位到了多个元素find_element默认取第一个偶尔也会造成误操作。这种情况可以用driver.find_elements()先确认页面上的实际数量。排查时最实用的技巧是在代码里加一行打印页面源码看这个元素到底存不存在# 观察定位失败瞬间的页面 HTML print(driver.page_source)6.2 浏览器驱动版本不匹配的处理方法报错信息里出现session not created: This version of ChromeDriver only supports Chrome version xxx时不要慌这是 Chrome 升级之后 chromedriver 版本老了的典型表现。解决办法有两种如果用了webdriver-manager直接清理缓存重新装最新驱动即可。如果手动管理驱动去对应官网下载与本地 Chrome 版本号完全匹配的驱动文件替换到系统 PATH 中。从长期来看我强烈建议统一使用webdriver-manager让驱动版本跟着浏览器版本自动走这个坑自然就消失了。6.3 接口测试偶发超时怎么办接口自动化偶发超时通常不是代码问题而是测试环境网络不稳定。这类问题我建议分三级处理请求时统一设置timeout10避免无限挂起。加一层重试机制网络瞬时抖动导致的失败不影响整体结果。如果重试后仍失败再去查接口返回的具体错误信息判断是环境问题还是业务 bug。实现简单的 HTTP 请求重试可以直接用requests的适配器from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry retry_strategy Retry(total2, backoff_factor1, status_forcelist[500, 502, 503]) adapter HTTPAdapter(max_retriesretry_strategy) http requests.Session() http.mount(http://, adapter) http.mount(https://, adapter)这里的逻辑是当遇到 500、502、503 这类服务器错误时重试 2 次第一次等待 1 秒第二次等待 2 秒采用指数退避策略。无论接口自动化还是普通爬虫这套配置都值得收藏。6.4 测试用例之间互相影响的问题耦合是自动化测试项目最常见的坏味道。比如用例 A 创建了一个账号用例 B 直接去用这个账号一旦 A 失败B 必然失败。这种连锁反应会让排查问题非常痛苦。我的经验是遵守一条铁律每个用例必须是独立的测试数据要么自己准备要么通过 fixture 现场创建。如果需要多个用例共享一份数据那共享的部分必须设计成幂等的比如登录 Token 可以共享因为它本身是只读凭据不会因为多次使用而失效。而订单、商品这类“创建型”数据尽量每个用例单独准备用完清理。6.5 稳定性提升的一些独家经验最后分享几个我在实际项目中摸索出来的、常规文档里不会写的技巧不要在用例里写time.sleep除了拖慢执行时间它只会在你的机器上“恰好能用”换一台机器或者网络波动立刻失效。不要让 UI 自动化断言页面截图上的文字很多前端用了字体图标同一个 class 在不同浏览器渲染效果完全不同这类断言只在恰好的环境里能过。测试数据要记得清理自动化跑多了脏数据会越来越多可能会污染后续的执行环境。每个用例尽量用独立的数据用例结束做数据清理。先跑接口自动化再跑 UI 自动化接口自动化的成本远低于 UI 自动化收益却更高。如果接口已经覆盖了核心业务逻辑UI 层只需要验证关键交互和页面跳转就够了。现在 AI 辅助编码的工具也越来越好用像 Codex、Cursor 这些工具已经能根据我的业务描述快速生成初始的测试脚本但生成的代码依然是“初稿”元素定位是否稳定、测试数据是否独立、断言是否精确这些都需要测试人员自己去审视和调整。自动化测试说到底拼的不是写代码的速度而是判断哪些地方该自动化、哪些地方不该自动化、怎么让用例在长时间运行中保持稳定。把上面这些基础打牢你搭建的测试体系就能在真实项目中真正站稳脚跟。
RELATED

相关推荐

微信小游戏开发实战:Unity从选型适配到变现的全流程指南

微信小游戏开发实战:Unity从选型适配到变现的全流程指南

确实,这两年微信小游戏成了很多独立开发者的第一站——不用装App、点开即玩、分享裂变天然成型,再加上一人工作室的极低成本结构,账户一申请、代码一提交、审核一过,就能直接面对几亿用户。我前后用Unity做了一款休闲建造类小游戏…

📅 2026/9/20 10:14:36
如何用 GetQzonehistory 备份 QQ 空间历史说说:从扫码登录到导出 Excel 的完整指南

如何用 GetQzonehistory 备份 QQ 空间历史说说:从扫码登录到导出 Excel 的完整指南

如何用 GetQzonehistory 备份 QQ 空间历史说说:从扫码登录到导出 Excel 的完整指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 想备份 QQ 空间说说,却找不到…

📅 2026/9/20 10:14:36
Flutter与HarmonyOS跨端游戏控制开发实践

Flutter与HarmonyOS跨端游戏控制开发实践

1. 项目概述:Flutter与HarmonyOS的跨端游戏控制实践在移动应用开发领域,跨平台技术正在重塑开发者的工作方式。作为一名长期从事移动开发的工程师,我发现Flutter与HarmonyOS的结合为游戏开发带来了全新的可能性。这次我们要实现的贪吃蛇游戏控…

📅 2026/9/20 10:14:36
MORE NEWS

更多资讯

📰

Codex MCP接入实战:从配置到排错全指南

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

📰

用Skills重构AI编程工作流:软件研发全生命周期的技能包实践

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

📰

硬件+订阅模式拆解:母婴品牌如何从0做到30万私域用户与高LTV

这些年我拆过不少私域案例,大部分都是“看上去很美”:社群拉了几万个,一算账全是靠补贴在撑。但“芙崽”这个牌子不一样,它从0做到30万私域用户,靠的不是单纯卖货,而是把硬件、订阅和用户生命周期价值&…

📰

RPA供应商选型:六项核心能力拆解与避坑实战

凡是最近在做流程自动化的企业,十有八九都在评估RPA。我观察到一个现象:很多公司的选型流程长得几乎一样——拉一张功能对比表,让供应商讲一轮PPT,约两个厂商做POC,然后拿着报价单去压价。这个流程不能说完全错了&…

📰

智能家居APP深度对比:米家、海尔智家、华为智慧生活怎么选?

家里的智能设备一多,手机里免不了要装好几个APP。单是“智能家居APP哪个好”这个问题,我在粉丝群里几乎每周都能看到有人问。有人图便宜买了一大堆杂牌插座和灯泡,结果发现每个牌子都要单独装一个APP,手机通知栏天天被各种“设备离…

📰

OpenCode配置文件opencode.json完全指南:从入门到避坑

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬