- 实现 MIoT 客户端核心功能(MQTT 连接、设备管理、属性读写) - 支持云端 API 调用与局域网设备发现(mDNS) - 集成国际化(i18n)多语言支持 - 添加 MIoT 设备规约解析器(spec_parser) - 包含单元测试与使用示例
21 KiB
21 KiB
go-miot 单元测试计划
状态: 待执行 | 创建: 2026-06-28 | 目标: 每个源文件一个测试文件,覆盖率 > 80%
目录结构约定
所有测试文件与源文件同目录,命名规则: {源文件名}_test.go
| 层次 | 包名策略 | 原因 |
|---|---|---|
| 纯逻辑/工具函数 | package miot_test (黑盒) |
只测公开API即可 |
| 客户端层(需访问内部锁/定时器) | package miot (白盒) |
需访问私有字段 |
现有 miot_test.go 稍后拆分为各独立 _test.go 文件。
Batch 1: 纯函数层 (无外部依赖) 🔴 P0
启动命令: 写完就能跑 go test -v -run "TestCtrlMode|TestError|TestCommon"
1.1 const_test.go — 常量与枚举
| 包名 | package miot_test |
|---|---|
| 依赖 | 无 |
| 难度 | ⭐ |
| # | 函数/值 | 测试内容 |
|---|---|---|
| 1 | CtrlMode.String() |
cloud/gateway/lan 三值 + unknown |
| 2 | SUPPORTED_PLATFORMS |
长度 > 0,包含 "switch", "light" 等关键值 |
| 3 | UNSUPPORTED_MODELS |
长度验证 |
| 4 | CLOUD_SERVERS |
包含 "cn", "us" 等 key |
| 5 | INTEGRATION_LANGUAGES |
包含 "zh-Hans", "en" |
| 6 | MIHOME_CA_CERT_STR |
非空,以 -----BEGIN CERTIFICATE----- 开头 |
| 7 | MIHOME_CA_CERT_SHA256 |
长度 = 64 (SHA256 hex) |
| 8 | RefreshPropsDelay |
= 200 |
| 9 | RefreshPropsRetryMax |
= 3 |
| 10 | TopicProp / TopicEvent / TopicState |
= "properties_changed" / "event_occured" / "state" |
1.2 miot_error_test.go — 错误类型
| 包名 | package miot_test |
|---|---|
| 依赖 | 无 |
| 难度 | ⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | NewMIoTOauthError(msg) |
返回 *MIoTOauthError,Code=CodeOauthUnauthorized,Message=msg |
| 2 | NewMIoTHttpError(msg) |
返回 *MIoTHttpError,Code=CodeHttpInvalidAccessToken |
| 3 | NewMIoTMipsError(msg) |
返回 *MIoTMipsError,Code=CodeMipsInvalidResult |
| 4 | NewMIoTDeviceError(msg) |
返回 *MIoTDeviceError,Code=CodeUnknown |
| 5 | NewMIoTSpecError(msg) |
返回 *MIoTSpecError |
| 6 | NewMIoTStorageError(msg) |
返回 *MIoTStorageError |
| 7 | NewMIoTCertError(msg) |
返回 *MIoTCertError,Code=CodeCertInvalidCert |
| 8 | NewMIoTClientError(msg) |
返回 *MIoTClientError |
| 9 | NewMIoTEvError(msg) |
返回 *MIoTEvError |
| 10 | NewMipsServiceError(msg) |
返回 *MipsServiceError |
| 11 | NewMIoTConfigError(msg) |
返回 *MIoTConfigError,Code=CodeConfigInvalidInput |
| 12 | NewMIoTOptionsError(msg) |
返回 *MIoTOptionsError |
| 13 | NewMIoTLanError(msg) |
返回 *MIoTLanError,Code=CodeLanUnavailable |
| 14 | (*MIoTError).Error() |
格式: "code={code}, message={msg}" |
| 15 | (*MIoTError).ToDict() |
返回 map,含 "code" 和 "message" key |
建议: 用 table-driven test, 12 个构造函数一个循环测完。
1.3 common_test.go — 工具函数
| 包名 | package miot_test |
|---|---|
| 依赖 | 文件系统 (os.ReadFile) |
| 难度 | ⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | CalcGroupID(uid, homeID) |
相同输入→相同输出;长度=16;hex字符串 |
| 2 | GenAbsolutePath(path) |
拼接正确;相对路径→绝对路径 |
| 3 | LoadJSONFile(path) |
有效JSON→正确解析;不存在文件→error;无效JSON→error |
| 4 | LoadYAMLFile(path) |
有效YAML→正确解析;不存在文件→error |
| 5 | RandomizeInt(value, ratio) |
结果在 [value*(1-ratio), value*(1+ratio)] 范围内 |
| 6 | RandomizeFloat(value, ratio) |
结果在有效范围内 |
| 7 | SlugifyName(name) |
"Hello World" → "hello_world";特殊字符处理;边界:空字符串 |
| 8 | SlugifyDID(server, did) |
= SlugifyName(server + "_" + did) |
Batch 2: 数据结构层 (轻依赖) 🔴 P1
2.1 miot_matcher_test.go — 订阅匹配树 (补充增强)
| 包名 | package miot_test (黑盒) |
|---|---|
| 依赖 | 无 |
| 难度 | ⭐⭐ |
现有测试已覆盖基本 Sub/Match/Unsub,需要补充:
| # | 测试项 | 说明 |
|---|---|---|
| 1 | # 通配符应匹配且是最后一段 |
did1/p/# 应匹配 did1/p/2/1 |
| 2 | # 不在最后一段→不匹配 |
did1/#/1 不应匹配 did1/p/2/1 |
| 3 | + 单层通配符 |
did1/+/2/1 应匹配 did1/p/2/1,不匹配 did1/p/x/2/1 |
| 4 | 完全不匹配 topic | did2/p/2/1 不应匹配 did1/p/2/1 |
| 5 | Clear() 清空订阅 |
Clear后 Size()=0,Match返回空 |
| 6 | Size() 计数器 |
多次 Sub 后 Size() 正确 |
| 7 | 同一个 handler 多次 Sub | 不重复计数 |
| 8 | 并发 Sub/Unsub/Match | -race 检测无 data race |
| 9 | Unsub 不存在的 ID | 不应 panic |
2.2 miot_storage_test.go — 文件存储
| 包名 | package miot_test |
|---|---|
| 依赖 | 文件系统 (临时目录) |
| 难度 | ⭐⭐⭐ |
使用 os.MkdirTemp 创建临时目录,defer os.RemoveAll 清理。
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | NewMIoTStorage(path) |
目录不存在时自动创建 |
| 2 | Save(domain, name, data) + Load() |
保存 string→读取正确;dict类型→正确解析 |
| 3 | Save nil data → error |
返回 *MIoTStorageError |
| 4 | Load 文件不存在 → (nil, nil) |
不返回 error |
| 5 | 数据完整性校验 | 修改文件尾部hash→Load返回nil(校验失败) |
| 6 | Remove(domain, name, typeName) |
删除后 Load 返回 nil |
| 7 | RemoveDomain(domain) |
递归删除整个目录 |
| 8 | FileExists(domain, name) |
存在→true,不存在→false |
| 9 | GetNames(domain, typeName) |
正确过滤后缀 |
| 10 | SaveFile / LoadFile |
无 hash 的原始文件读写 |
| 11 | Clear() |
清空所有文件,根目录保留 |
| 12 | GenStoragePath(domain, name) |
路径拼接正确 |
| 13 | LoadAsync / SaveAsync |
channel 返回正确结果 |
| 14 | UpdateUserConfig |
新建、合并、replace、删除(config=nil) |
| 15 | LoadUserConfig |
keys=nil→全部;keys=空切片→空map;指定keys→过滤 |
| 16 | NewMIoTCert |
storage=nil→error;uid为空→error |
| 17 | MIoTCert.CAFile/KeyFile/CertFile() |
路径正确 |
| 18 | VerifyCACert() |
首次运行写入嵌入CA→验证通过 |
| 19 | SaveUserKey/LoadUserKey |
保存→读取一致 |
| 20 | SaveUserCert/LoadUserCert |
保存→读取一致 |
| 21 | DidHash(did) |
稳定输出;相同输入→相同hash |
| 22 | DeviceManufacturer |
Init/DeInit/GetName/SaveManufacturerData |
| 23 | encodeToBytes / decodeByType |
bytes/str/dict/list 四种类型往返测试 |
2.3 miot_i18n_test.go — 国际化翻译
| 包名 | package miot_test |
|---|---|
| 依赖 | 文件系统 (i18n/ 目录下的JSON) |
| 难度 | ⭐⭐ |
| # | 测试项 | 说明 |
|---|---|---|
| 1 | NewMIoTI18n("en") |
创建实例 |
| 2 | Init() 加载 valid JSON |
data 非空 |
| 3 | Init() 文件不存在 |
data 保持空,不 panic |
| 4 | Translate 嵌套 key |
"device.light.name" → 遍历嵌套 |
| 5 | Translate key 不存在 → "" |
不 panic |
| 6 | Translate + replace map |
{key} 替换为 value |
| 7 | Deinit() |
data 清空 |
| 8 | Translate with non-string value |
返回 "" |
Batch 3: 业务逻辑层 (中等复杂度) 🟡 P2
3.1 miot_device_test.go — 设备模型
| 包名 | package miot_test |
|---|---|
| 依赖 | 需 *MIoTClient (可为 nil 测大部分) |
| 难度 | ⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | NewMIoTDevice(client, info, spec) |
从 map 解析所有字段: did/name/model/online/home_id/... |
| 2 | NewMIoTDevice model 含 "." → modelStrs 拆分 |
"lumi.plug.v1" → ["lumi","plug","v1"] |
| 3 | NewMIoTDevice float64 类型字段 |
connectType, rssi 从 float64 转换 |
| 4 | LoadFromCloud(client, cloudData) |
= NewMIoTDevice(client, cloudData, nil) |
| 5 | LoadSPEC(specInstance) |
nil→error;有效→serviceList 非空 |
| 6 | 所有 getter: DID()/Name()/Model()/Online()... |
返回正确值 |
| 7 | SetOnline(bool) |
getter 返回更新值 |
| 8 | SetSpecInstance(spec) |
SpecInstance() 返回更新值 |
| 9 | HasWiFi() |
connectType != -1 → true |
| 10 | ToDict() |
序列化正确;sub_devices 递归序列化 |
| 11 | FromDict(client, data) |
反序列化恢复所有字段 |
| 12 | AddSubDevice / GetSubDevice |
添加→读取;不存在→nil |
| 13 | SubscribeState / UnsubscribeState |
注册→通知 |
| 14 | NotifyStateChange(state) |
触发所有订阅者 |
| 15 | SubProp / UnsubProp |
client=nil→返回"";正常→返回subID |
| 16 | SubEvent / UnsubEvent |
同 SubProp |
3.2 spec_parser_test.go — SPEC 解析
| 包名 | package miot_test |
|---|---|
| 依赖 | 文件系统 (测试JSON spec文件), 网络 (Refresh 阶段) |
| 难度 | ⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | NewSpecStdLib(lang) |
lang="" → default "en" |
| 2 | SpecStdLib.Load(data) |
加载所有 6 种 map |
| 3 | SpecStdLib.Dump() |
Load→Dump 往返一致 |
| 4 | SpecStdLib.DeviceTranslate(key) |
命中→翻译;不命中→"" |
| 5 | SpecStdLib.ServiceTranslate |
同上 |
| 6 | SpecStdLib.PropertyTranslate |
同上 |
| 7 | SpecStdLib.EventTranslate |
同上 |
| 8 | SpecStdLib.ActionTranslate |
同上 |
| 9 | SpecStdLib.ValueTranslate |
同上 |
| 10 | SpecHash(typeStr, iids...) |
稳定输出 |
| 11 | SpecFormatGoType(format) |
"string"→"str", "bool"→"bool", "float"→"float", other→"int" |
| 12 | SpecPrecision(step) |
0→0, 0.1→1, 0.01→2 |
| 13 | parseAccess(interface{}) |
[]interface{}→[]string; string→[string]; nil→nil |
| 14 | parseValueRange(interface{}) |
dict格式→struct; list格式→struct; nil→nil |
| 15 | parseValueList(interface{}, ...) |
正常解析;重复描述去重 |
| 16 | strVal(m, key) |
存在→string; 不存在→""; 非string→fmt.Sprintf |
| 17 | intVal(m, key) |
float64/int/string/json.Number 四种输入 |
| 18 | boolVal(m, key) |
bool/string("true"/"1") |
| 19 | NewMIoTSpecParser(lang, storage, dir) |
参数正确赋值 |
| 20 | MIoTSpecParser.skipProprietary(item) |
proprietary=true→true; 不存在→false |
| 21 | MIoTSpecParser.readSpecFile(urn) |
有效URN→解析; 无效→error |
| 22 | MIoTSpecParser.parseSpec(urn, model, data) |
完整解析返回 MIoTSpecInstance |
| 23 | MIoTSpecParser.parseService |
device-information 类型跳过 |
| 24 | MIoTSpecParser.parseProperty |
type/description/format/access 任一缺失→nil |
| 25 | MIoTSpecParser.parseEvent |
type/description 任一缺失→nil |
| 26 | MIoTSpecParser.parseAction |
type/description 任一缺失→nil |
3.3 web_pages_test.go — 页面模板
| 包名 | package miot_test |
|---|---|
| 依赖 | embed 模板 |
| 难度 | ⭐ |
| # | 测试项 | 说明 |
|---|---|---|
| 1 | OAuthRedirectPage success=true |
STATUS_PLACEHOLDER → "true" |
| 2 | OAuthRedirectPage success=false |
STATUS_PLACEHOLDER → "false" |
| 3 | OAuthRedirectPage placeholder 替换 |
TITLE/CONTENT/BUTTON 正确替换 |
| 4 | HTML 不包含占位符 | 所有 PLACEHOLDER 被替换 |
| 5 | 边界: 特殊字符 title | 不破坏 HTML 结构 |
Batch 4: 客户端核心层 (需 Mock) 🟡 P3
4.1 miot_client_test.go — 客户端核心 (拆分自旧的 miot_test.go)
| 包名 | package miot (白盒) |
|---|---|
| 依赖 | MIoTStorage, MIoTHttpClient, MIoTMatcher |
| 难度 | ⭐⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | NewMIoTClient(entryID, data, uid, server, mode) |
所有字段初始化正确 |
| 2 | SetStorage / SetHTTPClient / SetMipsCloud / SetMipsLocal |
设置后可通过内部字段验证 |
| 3 | Init() |
entryData含 clientID+accessToken→http初始化 |
| 4 | Init() |
entryData不含凭证→http 为 nil |
| 5 | Deinit() |
timer 停止; subTree 清空 |
| 6 | loadCacheDevice() |
storage=nil→error |
| 7 | DeviceList() |
返回拷贝,修改不影响内部 |
| 8 | Start() |
Init + ScheduleRefreshDevices |
| 9 | Stop() |
= Deinit() |
4.2 miot_client_device_test.go — 设备管理
| 包名 | package miot |
|---|---|
| 难度 | ⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | GetDeviceInfo(did) |
存在→非nil;不存在→nil |
| 2 | GetDeviceInfos() |
返回拷贝 |
| 3 | LoadDevices() |
loadCacheDevice + RefreshDevices |
| 4 | RefreshDevices() |
http=nil→error |
| 5 | updateDeviceList(deviceInfos) |
新增设备; 更新已有设备; 删除不在云端的设备 |
| 6 | ScheduleRefreshDevices(delay) |
定时器正确调度 |
| 7 | OnDeviceStateChanged(did, state) |
更新 online 状态; 通知 subDeviceState |
4.3 miot_client_prop_test.go — 属性刷新
| 包名 | package miot |
|---|---|
| 难度 | ⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | RequestRefreshProp(did, siid, piid) |
添加到队列; 重复添加跳过 |
| 2 | refreshPropsHandler() |
队列为空→直接返回; http=nil→返回 |
| 3 | Timer 机制: 第一次 Request 创建 timer | 后续 Request 重用 timer |
| 4 | 并发 RequestRefreshProp |
无 panic, 队列完整 |
4.4 miot_client_sub_test.go — 订阅管理
| 包名 | package miot |
|---|---|
| 难度 | ⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | SubProp(did, handler, siid, piid) |
返回 subID; subTree 有记录; 触发 RequestRefreshProp |
| 2 | UnsubProp(did, subID) |
subTree 删除 |
| 3 | SubEvent(did, handler, siid, eiid) |
同上 |
| 4 | UnsubEvent(did, subID) |
同上 |
| 5 | OnPropMsg(params, ctx) |
解析 params; 匹配订阅者; 分发消息 |
| 6 | OnPropMsg params 含 "params" 包装层 |
正确解开 |
| 7 | OnEventMsg(params, ctx) |
同 OnPropMsg |
| 8 | checkDeviceState(did) |
三层状态检查 |
4.5 miot_client_api_test.go — API 调用
| 包名 | package miot |
|---|---|
| 难度 | ⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | SetProp(did, siid, piid, value) |
http=nil→error |
| 2 | GetProp(did, siid, piid) |
http=nil→error |
| 3 | Action(did, siid, aiid, inList) |
http=nil→error |
| 4 | SetProps(params) |
http=nil→error |
| 5 | GetProps(params) |
http=nil→error |
Batch 5: 网络层 (需 Mock HTTP/MQTT/mDNS) 🟡 P4
5.1 miot_cloud_test.go — 云API客户端
| 包名 | package miot_test |
|---|---|
| 依赖 | net/http/httptest |
| 难度 | ⭐⭐⭐⭐ |
使用 httptest.NewServer 模拟 API 响应:
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | NewMIoTOauthClient(...) |
host 计算正确 (cn vs 非cn) |
| 2 | MIoTOauthClient.GenAuthURL(...) |
URL 包含所有参数 |
| 3 | MIoTOauthClient.State() |
返回 hex 字符串 |
| 4 | parseInt64(s) |
正常解析; 含非数字字符; 空字符串 |
| 5 | toFloat64(v) |
float64/int/int64/string 四种输入 |
| 6 | toSlice(v) |
[]interface{}→正确; nil→nil |
| 7 | toStringSlice(v) |
[]string; []interface{}→[]string |
| 8 | isUnsupportedModel(model) |
命中→true; 不命中→false |
| 9 | containsString(slice, val) |
存在→true; 不存在→false |
| 10 | NewMIoTHttpClient(server, id, token) |
创建成功 |
| 11 | UpdateHTTPHeader(server, id, token) |
host/baseURL 更新 |
| 12 | apiRequestHeaders() |
包含 Authorization, X-Client-AppId 等 |
| 13 | mihomeAPIGet with mock server |
成功→解析; 401→error; 500→error |
| 14 | mihomeAPIPost with mock server |
同上 |
| 15 | GetProps(params) with mock |
正确构造请求; 解析响应 |
| 16 | SetProp(params) with mock |
正确构造请求 |
| 17 | DoAction(...) with mock |
正确构造请求; inList 处理 |
5.2 miot_lan_test.go — LAN 设备控制
| 包名 | package miot_test |
|---|---|
| 依赖 | 无 (纯逻辑函数) |
| 难度 | ⭐⭐⭐ |
优先测纯函数,避免真实网络:
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | LanDeviceState.String() |
fresh/ping1/ping2/ping3/dead/unknown |
| 2 | hexDecode(s) |
正常的32位hex→16字节; 奇数长度→error; 非法字符→error |
| 3 | md5Sum(data) |
已知输入→已知输出 |
| 4 | minFloat(a, b) |
a<b→a; b<a→b |
| 5 | mustParseUint64(s) |
"123"→123 |
| 6 | toInt(v) |
float64→int; int→int; string→0,false |
| 7 | isNumeric(s) |
纯数字→true; 含字母→false; 空→false |
| 8 | trimNull(b) |
尾部0去除 |
| 9 | LanDevice.GenPacket(out, msg) |
加密正确; packet 格式正确 |
| 10 | LanDevice.DecryptPacket(data) |
解密→原始消息; 错误MD5→error |
| 11 | newLanDevice(...) with valid token |
成功创建 |
| 12 | newLanDevice(...) with invalid hex |
error |
5.3 miot_network_test.go — 网络监控
| 包名 | package miot_test |
|---|---|
| 难度 | ⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | InterfaceStatus.String() |
add/update/remove/unknown |
| 2 | NetworkInfo.String() |
格式: "name: ip/mask (netseg)" |
| 3 | NewMIoTNetwork(nil, nil, 0) |
使用默认值 |
| 4 | NewMIoTNetwork 自定义 |
ipAddrList/urlAddrList 正确存储 |
| 5 | GetNetworkStatus() |
初始 false |
| 6 | SubNetworkStatus / UnsubNetworkStatus |
注册→注销 |
| 7 | SubNetworkInfo / UnsubNetworkInfo |
注册→注销 |
| 8 | UpdateAddrList(newIPs, newURLs) |
地址列表更新; 保留已有响应时间 |
| 9 | getNetworkInterfaces() |
返回非 nil; 过滤 loopback |
5.4 miot_mdns_test.go — mDNS 服务发现
| 包名 | package miot_test |
|---|---|
| 难度 | ⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | MipsServiceState.String() |
added/removed/updated/unknown |
| 2 | MipsServiceData.validService() |
role=1+suiteMQTT→true; 其他→false |
| 3 | MipsServiceData.ToDict() |
所有字段正确 |
| 4 | MipsServiceData.String() |
非空 |
| 5 | parseProfile(validB64) |
正确解析 did/groupID/role/suiteMQTT |
| 6 | parseProfile("") |
error: "empty profile" |
| 7 | parseProfile(invalidB64) |
error |
| 8 | parseProfile(too short) |
error: "profile too short" |
| 9 | reverseBytes(b) |
已知输入→已知输出 |
| 10 | strSlicesEqual(a, b) |
相等→true; 不相等→false; 长度不同→false |
| 11 | NewMipsService() |
创建成功 |
| 12 | GetServices("") |
初始为空 |
| 13 | SubServiceChange / UnsubServiceChange |
注册→注销 |
| 14 | entryToServiceData(entry) |
有效entry→正确解析; nil→error |
5.5 mips_client_test.go — MIPS 协议
| 包名 | package miot_test |
|---|---|
| 难度 | ⭐⭐⭐ |
| # | 函数 | 测试内容 |
|---|---|---|
| 1 | MIoTDeviceState.String() |
disable/offline/online/unknown |
| 2 | PackMipsMessage(mid, payload, from, retTopic) |
正确打包; 空payload→error |
| 3 | UnpackMipsMessage(data) |
打包→解包 往返一致 |
| 4 | MipsMessage.String() |
非空 |
| 5 | parseMQTTBroker(broker) |
"ssl://host:8883"→(host, 8883) |
| 6 | NewMipsCloudClient(broker, id, user, pass) |
创建成功 |
| 7 | NewMipsLocalClient(did, host, port, ca, cert, key) |
创建成功 |
| 8 | NewMipsDeviceState(did, online, source) |
字段正确 |
| 9 | newPahoMQTTClient(...) |
创建成功; Connect/Disconnect stub |
| 10 | pahoMQTTClient methods |
Connect→connected=true; Disconnect→connected=false |
测试执行命令参考
# 运行所有测试
go test -v -count=1 ./...
# 运行特定批次
go test -v -run "TestCtrlMode|TestError|TestCommon"
# 竞态检测
go test -race ./...
# 覆盖率
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# 只测试纯函数层(无需网络)
go test -v -short ./...
进度追踪
| 批次 | 状态 | 测试文件 | 测试函数数(预估) | 完成日期 |
|---|---|---|---|---|
| Batch 1 | ✅ 已完成 | const_test.go + miot_error_test.go + common_test.go |
~50 (1186行) | 2026-06-28 |
| Batch 2 | ✅ 已完成 | miot_matcher_test.go(增强) + miot_storage_test.go + miot_i18n_test.go |
~60 (1250行) | 2026-06-28 |
| Batch 3 | ✅ 已完成 | miot_device_test.go + spec_parser_test.go + web_pages_test.go |
~65 (950行) | 2026-06-28 |
| Batch 4 | ✅ 已完成 | miot_client_test.go + miot_client_device_test.go + miot_client_prop_test.go + miot_client_sub_test.go + miot_client_api_test.go |
~50 (920行) | 2026-06-28 |
| Batch 5 | ✅ 已完成 | miot_cloud_test.go + miot_lan_test.go + miot_network_test.go + miot_mdns_test.go + mips_client_test.go |
~55 (750行) | 2026-06-28 |
总计: 19 个测试文件, ~225 个测试用例, 覆盖 19 个源文件。
注意事项
- 旧的
miot_test.go: Batch 4 完成后可删除,内容已被拆分到各独立测试文件中 -short标志: 网络相关测试用testing.Short()跳过,go test -short只跑纯逻辑测试- 临时文件: 所有存储测试使用
os.MkdirTemp+defer os.RemoveAll - 并发测试:
miot_matcher和miot_client相关测试必须加go test -race验证 - Mock HTTP: Batch 5 的
miot_cloud_test.go使用httptest.NewServer,不产生真实网络请求