news 2026/9/11 23:16:26

authentik WebUI 测试体系完全指南:unit / browser / lit / blueprints 四种测试类型的选择、编写与运行

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
authentik WebUI 测试体系完全指南:unit / browser / lit / blueprints 四种测试类型的选择、编写与运行

authentik WebUI 测试体系完全指南:unit / browser / lit / blueprints 四种测试类型的选择、编写与运行

【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik

导读

本文以 web/test/AGENTS.md 为骨架,系统讲解 authentik 前端(WebUI)自动化测试体系的组织方式:test/unit(纯 Node 单元测试)、test/browser(驱动真实 UI 的浏览器端到端测试)、test/lit(Lit 组件渲染辅助)与test/blueprints(测试种子蓝图)四种形态如何划分边界、如何取舍、如何编写与运行。结合仓库中 web/test/unit/AGENTS.md、web/test/browser/AGENTS.md 以及web/e2e/web/test/下的真实源码与测试用例,你将掌握为 authentik WebUI 编写高质量自动化测试的完整规范,并能直接用文中命令跑通本地测试链路。

一、测试目录路由器:四种测试形态一览

authentik WebUI 是采用 Lit Web Components 与 PatternFly 4 设计系统的 TypeScript 单体仓库,包含 Flow(/if/flow/)、User(/if/user/)、Admin(/if/admin/)三个独立应用(详见 web/AGENTS.md)。与之匹配的自动化测试体系并不只有一层,而是四种各有其用途、各自有独立规范文档的形态。web/test/AGENTS.md正是这张"目录路由表"——在编写或修改任何测试之前,必须先阅读对应目录的规范文档。

目录存放内容运行器 / 环境约定文档
test/unit/纯 Node 测试:函数、类、模块,无 DOM 依赖Vitest,Node 环境web/test/unit/AGENTS.md
test/browser/端到端测试:在 Chromium 中驱动 admin 与 user UI 访问运行中的 authentik 实例Vitest browser provider(Playwright)+#e2efixturesweb/test/browser/AGENTS.md
test/lit/共享的 Lit 渲染辅助(renderLitLitViteContext),用于组件级浏览器测试,不直接存放测试
test/blueprints/YAML 蓝图(如test-admin-user.yaml),为浏览器测试提供认证所需的数据种子

从当前仓库目录结构可以确认这套体系已经完整落地:

  • web/test/unit/ 下已有lexer.test.tsflow-graph.test.tsflow-messages.test.tsauthenticator-validate-challenge-selection.test.tsevent-search.test.tslabels.test.tsunescape-locale-entities.test.ts以及maps/子目录下的bands.test.tswedges.test.tshexworld-style.test.ts等纯逻辑测试;
  • web/test/browser/ 下则是一组按编号与功能域组织的端到端套件:100-session.test.ts200-modals.test.ts300-users.test.ts400-groups.test.ts500-roles.test.ts600-providers.test.ts700-applications.test.ts800-rac.test.ts900-invitations.test.ts以及会话生命周期、passkey、路由基路径等专项套件;
  • web/test/lit/ 提供rendering.jssetup.js两个渲染辅助文件;
  • web/test/blueprints/test-admin-user.yaml 是唯一的种子蓝图,定义测试管理员账号。

web/test/CLAUDE.mdweb/test/unit/CLAUDE.mdweb/test/browser/CLAUDE.md仅以一行@AGENTS.md引用各自的规范文档,进一步印证了"规范入口唯一"的设计意图。

二、如何选择正确的测试形态:自上而下的决策清单

web/test/AGENTS.md给出了一条必须从上到下逐条匹配、命中即止的决策路径,这是整个测试体系最重要的心智模型:

  1. 纯函数、无 DOM、无网络?test/unit/。便宜、快速、适合高分支覆盖率。具体约定见 web/test/unit/AGENTS.md。
  2. 用户真实点击的功能流程(向导、对话框、导航、列表表格、登录)? →test/browser/。驱动真实 UI;禁止@goauthentik/api客户端写单元测试来"假装"覆盖 UI 流程。约定见 web/test/browser/AGENTS.md。
  3. 针对某个具体 Bug 的回归?找到test/browser/中它所属的功能套件,在现有文件中追加一个test(...)用例。不要新建以 Bug 命名的文件。如果 Bug 出在纯函数中,则往匹配的test/unit/文件里追加it(...)
  4. Lit 组件在隔离环境中的行为(组件生命周期、slots、事件、响应式更新,无整应用上下文)? → 在源码旁放置Component.browser.test.ts——Vitest 配置会匹配**/*.browser.test.ts,而 web/test/lit/setup.js 会向page暴露renderLit(...)用于挂载组件。由于目前还没有消费者,新增第一个用例前需要与团队确认。

如果你发现自己想做一件无法干净落入上述任何一个桶的事——例如写一个导入了 Lit 组件的单元测试,或者写一个通过 REST API 播种数据的浏览器测试——那正是"选错桶"的强烈信号,请回头重读目标桶的规范文档。

从仓库现状看,第 4 条已经出现了一个先行实践:web/test/component/ak-map.browser.test.ts 以ak-map.browser.test.ts的命名方式紧挨源码组件所在目录存放,印证了"colocate 组件级浏览器测试"的落地形态。

三、跨目录通用规则:所有测试都必须遵守

无论落在哪个桶,以下规则适用于test/下的一切代码:

  • 禁止自定义 API 客户端:绝不要在测试文件内构建基于fetch的管理端客户端。单元测试不需要它;浏览器测试必须驱动 UI;如果确实存在数据播种缺口,应扩展 fixture 或 blueprint 而不是写客户端。
  • 禁止硬编码凭据(fixture 中已有的除外):浏览器测试通过session.login()认证,使用 web/test/blueprints/test-admin-user.yaml 中的 bootstrap 管理员。不得在测试中读取process.env.AK_TEST_BOOTSTRAP_TOKEN
  • 实体命名确定性:浏览器测试创建数据时,必须用IDGenerator.randomID(...)保证唯一性(详见浏览器规范);单元测试永远不需要它。
  • 一个文件对应一个功能 / 符号:抵制创建以 Bug、工单号或日期命名的一次性文件。
  • 测试名称必须是完整句子:如"returns null once the input is exhausted""Create application with existing provider"。不能是"works",更不能是"#22383"

其中"确定性命名"在源码中有清晰的落点:randomNameIDGenerator由 web/e2e/utils/generators.ts 提供。其实现用种子字符串做 32 位简单哈希(hash * 31 + charCode),再对形容词、颜色字典长度取模,从而把同一seed稳定映射到同一组短语,再经capitalCase生成形如 "Amber Quiet Falcon" 的可读名称。

四、运行方式:一条命令到单文件过滤

npm test # 同时运行两个项目(unit + browser) npx vitest run test/unit # 只跑单元测试 npx vitest run test/browser # 只跑浏览器测试 npx vitest run path/to/single.test.ts # 只跑单个文件 npm run test:e2e # Playwright e2e CLI 路径(同一份 test/browser 源码)

浏览器测试要求有一个可运行的 authentik 实例,地址由AK_TEST_RUNNER_PAGE_URL指定(默认http://localhost:9000)。如果实例未就绪,web/test/browser/prerequisites.setup.ts 中的健康检查会立即报错退出。

单元测试的快速迭代则另有更细的过滤手段(见 web/test/unit/AGENTS.md):

npx vitest run test/unit/lexer.test.ts # 单文件 npx vitest test/unit/lexer.test.ts -t "tokenization" # 按名称过滤

npm test脚本同时驱动 unit 与 browser 两个 Vitest 项目;纯逻辑改动建议直接跑单文件以获得最快反馈。npm run test:e2e走 web/playwright.config.js 配置的 Playwright CLI 路径,对 Chromium 配置了"首次重试时记录 trace"与深色配色方案。

五、单元测试(test/unit):纯逻辑的快速验证层

5.1 定位与适用场景

单元测试是纯 Node、无浏览器的测试:针对独立函数、纯逻辑和无 DOM 依赖的模块,运行在 Vitest 的 Node 环境下——没有 Playwright、没有 Lit 渲染、没有运行中的 authentik 实例。适合:

  • 被测对象是普通函数或类,无 DOM、无网络、无组件生命周期;
  • 全面而快速地覆盖分支、边界、错误路径与不变式;
  • 给定输入行为确定——没有定时器、没有外部服务、没有customElements.define

一旦答案涉及渲染 Lit 组件、点击、等待网络或对 DOM 断言,就不属于这里,应推给 colocate 的 Lit 组件测试或test/browser/

5.2 文件布局与导入

  • 文件位于test/unit/*.test.ts一个文件对应一个被测模块/特性,以符号或模块命名(如lexer.test.ts);
  • Vitest 配置同时匹配全工作区的**/*.unit.test.ts,因此紧密耦合的测试也可以作为foo.unit.test.ts放在源码旁边,比塞进平行的test/unit/目录更清晰;
  • 必须从vitest导入describe/it/expect/vi严禁#e2e导入test/expect(那是浏览器测试专用的,会拖入 Playwright);
  • 通过包别名#flow/…#elements/…#common/…访问源码,绝不使用指向src/的相对路径。这与 web/AGENTS.md 中"导入优先使用package.json#imports定义的别名"的整体约定一致,确保测试从任何位置(包括测试目录)都能正确解析导入;
  • vi用于 spy、mock 与定时器;优先使用真实实现,只在真正成问题的模块边界(网络、时间、随机性)处打桩。

5.3 测试结构与断言惯例

import { describe, expect, it, vi } from "vitest"; import { shouldResetSelectedChallenge } from "#flow/stages/authenticator_validate/challenge-selection"; describe("shouldResetSelectedChallenge", () => { it("returns true when the previously selected challenge is no longer allowed", () => { const selected = makeDeviceChallenge(DeviceClassesEnum.Email, "email-1"); const allowed = [ makeDeviceChallenge(DeviceClassesEnum.Totp, "totp-1"), makeDeviceChallenge(DeviceClassesEnum.Webauthn, "webauthn-1"), ]; expect(shouldResetSelectedChallenge(selected, allowed)).toBe(true); }); it("returns false when the previously selected challenge is still allowed", () => { ... }); it("returns false when there was no selected challenge", () => { ... }); });

要点:

  • 顶层describe(symbolName),可按方法或行为再嵌套(参考 web/test/unit/lexer.test.ts 中的describe("tokenization")等写法);
  • it("returns X when Y")——以动词开头的完整句子,同时写明结果与前置条件
  • 采用 Arrange / act / assert 三阶段,阶段之间用空行分隔;重复的测试数据形状用内联工厂(如makeDeviceChallenge(...)),放在文件顶部,直到两个文件需要同一形状才抽到共享 helper;
  • 每个it只测一个概念,一旦名字里出现 "and" 就该拆分;
  • 单元测试中expect()不加断言消息——测试名与匹配器已足以表达意图,Vitest 输出足够清晰。

匹配器选型:toBe用于原始值与引用同一性,toEqual用于结构相等,toThrow(/regex/)用于错误路径(匹配消息的稳定片段而非整句),.mock.calls[i]?.[j]精确断言 spy 参数。mocking 上优先用vi.fn()内联构造测试替身而非模块级vi.mock(...);只有被测代码真正读时钟时才用vi.useFakeTimers();确实需要vi.mock("module")时提升到文件顶部并在理由不明显处加一行注释说明"为什么"。

5.4 单元测试的禁区

  • @playwright/test#e2e导入(浏览器测试专属);
  • 调用customElements.define导入 Lit 组件(Node 环境无 DOM,组件覆盖应交给test/browser/或 colocate 的.browser.test.ts);
  • 访问网络或文件系统——纯函数测试;如果单元需要 IO,说明测错了层级;
  • try/catch静默吞错——错误路径用expect(() => …).toThrow(...),缺失的 throw 必须让测试失败;
  • 对快照断言,除非输出是稳定且有意为之的产物(例如 token 流)——快照在替代对契约的思考时腐烂得很快。

六、浏览器端到端测试(test/browser):驱动真实 UI

6.1 核心哲学:驱动 UI,而非 API

浏览器测试是运行在 Vitest browser runner(Chromium)下的 Playwright 测试,针对运行中的 authentik 实例端到端地演练 admin 与 user UI。其哲学有三条,决定了整个目录的写作风格:

  • 驱动 UI,而不是 API:功能的测试必须走用户路径——点 "New Provider"、填表单、点 "Create"、验证出现。不通过 REST API 播种实体再点一个按钮验证单一副作用。如果 UI 流程坏了,测试必须一起坏;如果走 API 捷径,向导、模态框、导航和表单绑定中的回归就会漏网。
  • 覆盖功能,而不是 Bug:测试文件以功能命名(providers.test.tsapplications.test.ts),不以 Bug 命名。针对特定缺陷的回归测试属于该功能既有套件中的一个额外test(...)用例,而不是带临时 API 管道的独立文件。
  • 无自定义 HTTP 客户端、无显式清理:如果在测试里写出makeAPIClient,停下——要么驱动 UI 创建前置状态,要么扩展 fixture 使其可复用。实体名用IDGenerator.randomID(...)播种,每次运行产生唯一 slug,历史运行遗留的实体不会冲突,允许在开发环境自然累积;不要写try/finally清理块,它会掩盖断言失败并吞掉真实故障。

6.2 导入方式与 fixture 体系

测试从#e2e别名导入,从不直接导入@playwright/test

import { expect, test } from "#e2e"; import { randomName } from "#e2e/utils/generators"; import { IDGenerator } from "@goauthentik/core/id"; import { series } from "@goauthentik/core/promises";

#e2e的入口 web/e2e/index.ts 重导出 Playwright 的expect,并导出一个经 fixtures 扩展的test——它基于base.extend<E2EFixturesTestScope, E2EWorkerScope>(...)注册了navigatorsessionformpointerpasskey五类 fixture,每个测试运行时都会按需构造。

从测试回调中解构你需要的 fixture,全部按测试逐一构造:

Fixture用途
sessionlogin({ to, username?, password?, rememberMe? })toLoginPage()checkAuthenticated()。默认使用test-admin@goauthentik.io/test-runner(即 blueprint 中播种的 bootstrap 管理员)
navigatornavigate(to)waitForPathname(to)——优先于page.goto,保证 URL 等待的一致性
formfill(label, value, ctx?)search(query, ctx?)selectSearchValue(label, pattern, ctx?)setInputCheck(label, bool, ctx?)setRadio(group, name, ctx?)setFormGroup(pattern, open, ctx?)。理解ak-switch-inputak-form-group与 search-select 下拉
pointerclick(name, role?, ctx?)——按可访问名称的高层点击,默认按钮/链接
page原始 PlaywrightPage,用于 fixture 覆盖不到的地方;Shadow DOM 自动穿透
baseURL实例 URL,来自AK_TEST_RUNNER_PAGE_URL(默认http://localhost:9000

大多数步骤应经由formpointer完成;只有不存在对应 fixture 方法时才使用page.locator(...)

SessionFixture(web/e2e/fixtures/SessionFixture.ts)给出了"驱动 UI 登录"的源码级细节:它定位ak-stage-identification、按getByLabel("Username")找用户名框、getByLabel("Password", { exact: true })精确匹配密码框(避免与"Show password"开关的 substring 冲突)、getByRole("checkbox", { name: "Remember me on this device" })处理记住我,登录流程路径固定为/if/flow/default-authentication-flow/,并对认证失败用getByRole("alert", …)捕获。整套选择器策略正是规范中"ARIA role 优先、精确匹配防歧义"的实践范本。

6.3 测试结构与test.step分段

test.describe("Feature name", () => { const names = new Map<string, string>(); test.beforeEach("Seed names", async ({ page: _page }, { testId }) => { const seed = IDGenerator.randomID(6); names.set(testId, `${randomName(seed)} (${seed})`); }); test("Do the thing", async ({ session, navigator, form, pointer, page }, testInfo) => { const name = names.get(testInfo.testId)!; const { fill, search, selectSearchValue } = form; const { click } = pointer; await test.step("Authenticate", async () => { await session.login({ to: "/if/admin/core/providers" }); }); const dialog = page.getByRole("dialog", { name: "New Provider Wizard" }); await test.step("Open wizard", async () => { await expect(dialog, "Wizard is initially closed").toBeHidden(); await click("New Provider"); await expect(dialog, "Wizard opens").toBeVisible(); }); await test.step("Fill form", async () => { await series( [click, "OAuth2/OpenID", "option"], [fill, "Provider Name", name], [ selectSearchValue, "Authorization Flow", /default-provider-authorization-explicit-consent/, ], [click, "Create"], ); }); await test.step("Verify created", async () => { await expect(await search(name), "Provider is visible").toBeVisible(); }); }); });

规范中强调的约定包括:

  • 每个功能一个test.describe,测试用祈使式命名;
  • 每个有意义的阶段一个test.step(...)——它们会出现在 trace 与 HTML 报告中,让失败自动定位;
  • 名称用模块级MaptestId缓存,在beforeEach中填充;
  • series([fn, ...args], ...)以自上而下可读的用户动作脚本形式编排有序表单填写序列;
  • 对话框定位器只捕获一次,再作为ctx?参数传入其内部的fill/click/selectSearchValue以限定作用域;
  • 每个expect都要带第二个参数作为消息,以被断言的属性来措辞("Wizard opens"、"Provider is visible"),而不是复述匹配器;
  • 第一个参数必须是解构模式,即使不引用任何 fixture 也要写async ({ page: _page }, { testId }) => {…}——裸标识符会触发 Playwright 运行时的First argument must use the object destructuring pattern(它靠解析参数模式决定注入哪些 fixture),而空解构async ({}, { testId }) => {…}又会触发 ESLint 的no-empty-pattern;解构并重命名是唯一同时满足两者的形式。

真实的 web/test/browser/700-applications.test.ts 完整复刻了这一骨架:test.describe("Applications")+ 模块级providerNamesMap +beforeEach中用IDGenerator.randomID(6)播种、randomName(seed)生成可读名称,随后按test.step依次"创建 OAuth2 provider""验证 provider 创建""导航到 applications""创建 application",全程只通过对话框、按钮、表单与 search 交互。

6.4 定位器优先级与断言

定位器按以下顺序选用:

  1. ARIA role 查询page.getByRole("button", { name: "Create" })page.getByRole("dialog", { name: /Launch Endpoint/i })page.getByLabel("Username")——能抗住样式/标记变更并表达意图;
  2. Web 组件标签page.locator("ak-stage-identification")page.locator("ak-form-group", { hasText: /Advanced/ })——稳定的元素契约;
  3. data-test-idpage.getByTestId("..."),Playwright 配置了testIdAttribute: "data-test-id",仅当 role/label 无法区分时才新增测试 id;
  4. CSS 选择器:最后手段。

Shadow DOM 是透明穿透的——不要写.shadowRoot遍历。断言时始终传消息;只有默认 5 秒确实不够时才显式{ timeout: ... }(通常是对话框挂载或导航这类异步 UI 转换后的首个断言);不要加page.waitForTimeout,等待你真正关心的定位器条件。

6.5 反模式清单

  • 测试文件内的自定义 API 客户端:无makeAPIClient,无fetch(${baseURL}/api/v3/...)做 setup;
  • 从测试中读取process.env.AK_TEST_BOOTSTRAP_TOKEN——测试应通过session.login()以真实用户身份认证;
  • 针对单一 Bug 的一次性回归文件——改为在相关功能套件中追加test(...)用例;
  • try/finally清理块——名称已随机化,让实体自然累积;
  • 无等待的page.goto——用navigator.navigate(to)session.login({ to })
  • 存在 role/label 时仍对 CSS 选择器断言;
  • 跳过test.step——冗长扁平的测试难以调试,每个阶段都要包裹。

6.6 新增覆盖的路径

扩展既有套件时,跟随周边模式——同样的 fixture 解构、同样的Map<testId, name>风格、同样的"对话框即上下文"惯用法。引入新套件时以applications.test.tsproviders.test.ts为规范范例建模。如果需要尚不存在的 helper(新的表单输入形态、新的通用导航),应扩展 web/e2e/fixtures/ 中的 fixture,而不是在测试里复制逻辑。

七、Lit 组件渲染辅助(test/lit):组件级浏览器测试的基础设施

test/lit/不直接存放测试,而是提供两个共享渲染辅助:web/test/lit/rendering.js 导出LitViteContext(含rendercleanup),web/test/lit/setup.js 在 Vitest 的 browser 环境下把渲染能力挂到页面对象上:

import { LitViteContext } from "./rendering.js"; import { beforeEach } from "vitest"; import { page } from "vitest/browser"; page.extend({ // @ts-expect-error Extension is not properly typed. renderLit: LitViteContext.render, [Symbol.for("vitest:component-cleanup")]: LitViteContext.cleanup, }); beforeEach(() => LitViteContext.cleanup());

也就是说,任何 colocate 的*.browser.test.ts都能通过page.renderLit(...)挂载组件,且每个用例前自动清理上一次渲染的 DOM。这正好支撑了"决策清单第 4 条"的落地——目前仓库中已出现一个实践样本 web/test/component/ak-map.browser.test.ts,但尚无更多消费者,因此新增第一个组件级用例前需要与团队确认方向。

八、测试种子蓝图(test/blueprints):浏览器测试的认证基石

web/test/blueprints/test-admin-user.yaml 是一个 authentik 蓝图(blueprint)格式的 YAML 文件,为浏览器测试播种 bootstrap 管理员:

version: 1 entries: - attrs: email: test-admin@goauthentik.io is_active: true name: authentik Default Admin password: test-runner path: users type: internal groups: - !Find [authentik_core.group, [name, "authentik Admins"]] conditions: [] identifiers: username: akadmin model: authentik_core.user state: present

它定义了用户名akadmin、邮箱test-admin@goauthentik.io、密码test-runner,并把该用户通过!Find表达式挂到authentik Admins组——这正是SessionFixtureGOOD_USERNAME/GOOD_PASSWORD(web/e2e/fixtures/SessionFixture.ts)与session.login()默认凭据的数据来源。当浏览器测试遇到"需要先有某种前置数据"的缺口时,正确做法是扩展这样的蓝图或 fixture,而不是在测试里硬编码凭据或调用 API。

九、文件与功能速查

按 web/test/AGENTS.md 的 "Where things live" 一节,各功能部件的最终落点汇总如下:

  • Playwright fixturessessionnavigatorformpointerpasskey)与#e2e入口: web/e2e/index.ts 及 web/e2e/fixtures/(SessionFixture.tsNavigatorFixture.tsFormFixture.tsPointerFixture.tsPasskeyFixture.tsPageFixture.ts);
  • Lit 渲染辅助: web/test/lit/setup.js 与 web/test/lit/rendering.js;
  • 种子蓝图(测试管理员等): web/test/blueprints/test-admin-user.yaml;
  • 生成器IDGeneratorrandomName): web/e2e/utils/generators.ts 与@goauthentik/core/id(字典文件位于web/e2e/utils/dictionaries/下的adjectives.txtcolors.txt);
  • 浏览器测试健康检查(负责在实例未就绪时"响亮地失败"): web/test/browser/prerequisites.setup.ts。

十、总结:一张路由表驱动整个测试体系

authentik WebUI 的测试体系本质上是一个**"按形态路由"**的体系:web/test/AGENTS.md是入口路由表,unitbrowser两份规范文档分别是纯逻辑层与 UI 行为层的"宪法",litblueprints是支撑组件级测试与认证前置的基础设施,e2e/则承载所有可复用的 fixture 与生成器。

判断一个测试该写在哪里的口诀可以浓缩为:有 DOM 交互去browser,纯逻辑去unit,组件隔离行为 colocate 成.browser.test.ts,数据缺口补 blueprint,能力缺口扩 fixture,永远不要在自己的测试文件里发明 API 客户端。遵循这张路由表与两条规范文档,你写出的测试将天然具备确定性命名、可自定位的test.step分段、消息完备的断言,以及"驱动 UI 而非 API"的回归保障能力。

【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 23:12:09

机械毕业设计之C6132普通车床横向进给系统的数控改造

题目&#xff1a;毕业设计之C6132普通车床横向进给系统的数控改造一、项目介绍针对C6132普通车床横向进给系统加工精度低、生产效率不足、手动操作劳动强度大等问题&#xff0c;结合中小制造企业经济性需求&#xff0c;开展其数控改造研究。首先&#xff0c;通过现场检测明确原…

作者头像 李华
网站建设 2026/9/11 23:10:46

焊缝X射线底片缺陷检测实战:2647张数据集与YOLOv8训练全流程

简介&#xff1a;面向工业焊缝缺陷检测任务的目标检测数据集&#xff0c;包含2647张X射线底片清晰原图&#xff0c;对裂纹、未熔合、未渗透、空隙、夹渣及未融化6类缺陷进行矩形框标注&#xff0c;累计4766个目标框。数据采用VOC与YOLO双格式存储&#xff0c;图片、XML标注与TX…

作者头像 李华
网站建设 2026/9/11 23:09:21

Android投屏源码实战:基于Qt的H.264实时采集、UDP传输与渲染

简介&#xff1a;基于Qt的Android实时投屏软件源码项目&#xff0c;专为需要将Android手机画面实时同步至PC桌面的开发者准备&#xff0c;适配毕业设计、课程大作业、初期项目演示等场景&#xff0c;也适合具备一定C/Qt基础的学生进阶学习。资源包共31个文件&#xff0c;包含12…

作者头像 李华
网站建设 2026/9/11 23:08:09

SpringBoot旅游平台开发实战:UGC社区架构与优化

1. 项目概述&#xff1a;SpringBoot旅游攻略分享平台 这个旅游攻略分享平台是我去年带队开发的一个实战项目&#xff0c;核心目标是为旅行爱好者构建一个内容共创社区。不同于传统的旅游资讯网站&#xff0c;我们采用SpringBootVue前后端分离架构&#xff0c;实现了用户生成内容…

作者头像 李华