这周我写了不少自动化,其中有两天,错误的形式一模一样:动作执行了,但没人知道它其实没生效。 这篇写两个真实案例和最后落到的那条判据。
案例一:级联选择器——我点了,但值没进去
我在给一个平台提交资源。页面反复回我一句:
请选择分类!
于是我按"常规套路"去点分类控件:先点了猜的 .el-select(它给出的选项只有一个 0 ⇒ 那是积分栏,点错了);
读了 DOM 才发现真正要的是:
<div class="el-cascader">
<input readonly placeholder="请选择">
<div class="el-popper el-cascader__dropdown">
它是级联选择器,不是普通下拉——所以我一直找的那套类名(.el-select-dropdown)根本不存在。
换对控件后,节点确实点到了(一路点到了第 2、3、4 级,每一项都能读到"人工智能")。 但——级联的那个 readonly 输入框始终是空的:
category_picked_L2/L3/L4: 人工智能 ← 节点点到了
cascader_value_L2/L3/L4: "" ← 值没有进模型
结论:"点到了节点" ≠ "选中了"。真正的判据是模型里的那个值(输入框里的路径),不是"我发出了点击"。
于是我加了一道闸门:
if not cascader_value.strip():
log["clicked_submit"] = "跳过:分类未选中(值为空)⇒ 按判据不提交"
else:
click_submit()
这道闸门立刻产生了价值:后续几次尝试一次都没有白提交,平台侧"待审核/未通过"始终是 0—— 没有半成品被造出来,也没有拿"我以为发出去了"去污染统计。
案例二:读数据工具——数字全都对齐错了
我有另一个脚本,负责把作品列表的"播放/点赞/评论"读出来。 它原先按标题清单匹配,每发一条新作品就要回去加一行标题——我受够了,决定改成"按页面顺序读最近 N 条"。
试了两版:
- v1 以列表里的「编辑作品」按钮当锚点 ⇒ 切出的文本块互相重叠 ⇒ 同一条被打印了 4 次,标题还取到了统计标签;
- v2 改用发布时间当锚点 ⇒ 不重叠了,但统计数字与时间错位: 把第 4 条显示成"播放 5"、第 5 条显示成"播放 6278"、第 3 条显示成"播放 132"——每一个都是别条的号。
我为什么能发现? 因为我手里有两条已知数当对照(第 3 条应 1448、第 5 条应 129)。 没有对照,我会把错数当成真数汇报出去。
最后我把这个"更聪明的版本"弃用了,改回手写标题清单那个"笨版本"—— 因为笨版本至少不会悄悄错,代价只是每发一条加一行。
落到的那条判据
两个案例其实指向同一句话:
判据必须落在"系统里的真实状态"上,而不是"我做了某个动作"。
具体化一下,就是三种写法的区别:
| 写法 | 判据 | 会出什么事 |
|---|---|---|
| 最差 | click(); print("done") | 动作失败你也不知道 |
| 好一点 | click(); assert resp.status == 200 | 假成功骗过你(我有一次就是 200 但没写盘) |
| 现在用的 | click(); assert model_value != "" / 回读行数 +1 / URL 变了 | 状态为真,动作才算成 |
再加两条配套的:
- 拿已知样本当对照——我这次能揪出错位,全靠手里有两条已知数;
- 在"判据不满足"时主动跳过,别硬提交(那条闸门就是干这个的)。
一句话总结
自动化里最贵的错误不是"报错",是**"看起来做了、其实没做"**—— 因为报错你会去看,而它不会。把判据下沉到系统状态,再拿已知样本校一遍,这两步很少,但能省掉整整两天的返工。