From abcb5669b883c39653d1705292cbee5ca0b9f738 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Wed, 4 Nov 2026 18:30:11 +0800 Subject: [PATCH] =?UTF-8?q?docs(lessons):=20=E8=A1=A5=20=C2=A75.3.1=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=BD=8D=E7=BD=AE=E7=9A=84=E4=B8=89=E7=A7=8D?= =?UTF-8?q?=E5=AE=9E=E9=99=85=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按 §5.3 动手后撞到的三类不可迁移情况,记清楚避免以后重复摸索: 可行 /test/ 子目录(实测 pkg/test/ 里写 package pkg 也访问不到 未导出符号 —— Go 按目录划分包实例,所以与外部包等价) 不可行 package main(CLI)—— 'is a program, not an importable package' 不可行 测试直接构造私有字段 —— 但多数情况下导出构造入口已接受这些 参数(如 SDK 的 New(...)),改完判据仍咬得住变异 不可行 判据扫描「当前目录源码」—— 迁到子目录后 glob 到空,静默失去对象 以及迁移顺序:必须一次一个、让编译器说话。批量脚本加包名前缀会改到 导入路径/注释/标准库符号(实测把 core 改成 core.core.core,误伤 strconv)。 --- docs/zh/engineering-lessons.md | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/docs/zh/engineering-lessons.md b/docs/zh/engineering-lessons.md index e08713c7..dbc8ff00 100644 --- a/docs/zh/engineering-lessons.md +++ b/docs/zh/engineering-lessons.md @@ -294,6 +294,64 @@ internal/plugins/webui/ 29 个测试文件,全部 package webui(白盒) **这不是风格问题**:一个包里如果 90% 判据是白盒,这个包的模块边界 事实上不存在 —— 谁都不敢改内部实现。 +### 5.3.1 三种测试位置的实际边界(2026-11-04 实测) + +写完 §5.3 之后按它动手,撞到三类不可迁移的情况。这里把边界记清, +避免以后有人(包括我)以为"所有测试都能挪到 test/"。 + +#### 可行:`/test/` 子目录 + +Go 允许同目录下 `pkg` 与 `pkg_test` 两个包名,也允许 `/test/` 放 +独立包。**关键事实**:`pkg/test/` 里写 `package pkg` 也访问不到未导出符号 —— + +```go +// pkg/test/a_test.go +package pkg +func TestX(t *testing.T) { _ = internalVar } // undefined: internalVar +``` + +Go 按**目录**划分包实例,不同目录就是不同包。所以"子目录"和"外部包" +在可见性上等价,都强制黑箱。 + +#### 不可行一:`package main`(CLI) + +``` +import "x/cmd" is a program, not an importable package +``` + +`tools/hmapdev`(19 个测试文件)、`cmd/homed` 等属于这类,**必须留在包内**。 + +#### 不可行二:测试直接构造私有字段 + +SDK 原测试写 `&PluginSDK{regStage: regStage, name: "test"}` —— 迁到外部包后 +连**构造被测对象**都做不到(`cannot refer to unexported field regStage`)。 + +**但这不等于必须白盒**:`New(name, sett, regTool, regStage, regAPI, regOutput)` +本来就把这些作为参数,等价且是生产路径。实测 19 处全部可改,改完判据仍然 +咬得住变异。 + +⇒ 遇到"要设私有字段"时,先问:**有没有一个导出的构造入口已经接受这些参数?** +大多数情况有,只是测试偷懒没用。 + +#### 不可行三:判据扫描"当前目录源码" + +```go +files, _ := filepath.Glob("*.go") // childkind_test.go +``` + +迁到子目录后 glob 到**空目录**,判据静默失去对象 —— 不报编译错,直接绿。 +这类要先改成"扫描固定相对路径",或者干脆按 §5.2 重写成行为判据。 + +#### 迁移顺序:一次一个,让编译器说话 + +第一版我批量迁 11 个文件、再用脚本自动加 `core.` 前缀,结果把 +`import "…/internal/agent/core"` 本身也改了(变成 `core.core.core`), +还误伤 `strconv.Itoa` 等标准库符号。 + +文本替换在"加包名前缀"这种场景不可靠 —— 它会改到导入路径、注释、 +标准库符号。**正确做法是一次只迁一个,逐步看编译器报什么,手工修。** +慢,但不会留下半成品(agent/core 已因此完全回滚过一次)。 + ### 5.4 CI 必须覆盖每个独立 module **实测缺口(2026-11-04)**:主仓有 `third_party/hologramagentsdk/`,