有一种Bug,最容易让测试工程师怀疑人生:
接口测过了。
数据库查过了。
自动化回归也是绿的。
甚至开发信誓旦旦地说:
“后端返回的数据肯定没问题。”
结果产品经理打开页面,只看了一眼:
“这个版本不能上。”
问题出在哪?
不是接口500。
不是数据库脏数据。
不是按钮点不了。
而是——
用户看到的东西错了。
这也是上一期我们聊完DeepSeek视觉模型之后,我觉得更值得继续往下讨论的问题:
当多模态模型开始进入UI自动化,它真正应该解决的,并不是“帮测试工程师看截图”,而是过去传统自动化很难理解的“页面业务语义”。
今天不讲Demo。
直接看一个真实业务里非常典型的场景。
一、一个很普通的电商结算需求
假设现在要测试一个商城结算页。
用户购买一件1000元的商品。
业务规则:
商品原价:1000元
VIP优惠:-100元
优惠券:-50元
运费:+10元
最终应付:860元
后端接口返回:
{
"original_price": 1000,
"member_discount": 100,
"coupon_discount": 50,
"shipping_fee": 10,
"pay_amount": 860
}
接口自动化怎么写?
非常简单:
def test_checkout_amount(data):
expected = (
data["original_price"]
- data["member_discount"]
- data["coupon_discount"]
+ data["shipping_fee"]
)
assert expected == data["pay_amount"]
结果:
expected = 860
actual = 860
PASS
再查数据库:
SELECT
order_id,
original_price,
discount_amount,
shipping_fee,
pay_amount
FROM orders
WHERE order_id = 'ORDER_10086';
结果:
original_price = 1000
discount = 150
shipping_fee = 10
pay_amount = 860
还是:
PASS。
到这里,大部分传统接口测试都会认为:
订单金额计算没问题。
二、再加一层UI自动化,还是绿的
我们继续用Playwright验证页面。
from playwright.sync_api import expect
def test_checkout_page(page):
page.goto(
"https://test.example.com/checkout"
)
expect(
page.get_by_test_id("original-price")
).to_have_text("¥1000")
expect(
page.get_by_test_id("discount")
).to_have_text("-¥150")
expect(
page.get_by_test_id("shipping")
).to_have_text("¥10")
expect(
page.get_by_test_id("pay-amount")
).to_have_text("¥860")
全部通过。
现在已经有三层证据:
API PASS
Database PASS
DOM PASS
按传统自动化测试的逻辑,这个Case基本可以结束了。
但是实际页面可能长这样:
商品原价 ¥1000
会员+优惠券 -¥150
运费 ¥10
应付金额 ¥860
¥1000
[ 提交订单 ]
问题来了。
¥860下面,又叠了一个¥1000。
对于测试脚本来说:
pay-amount = 860
确实存在。
所以PASS。
但是对于用户来说,他现在看到的是:
我到底要付860,还是1000?
如果这是支付确认页,这已经不是一个普通“样式不好看”的Bug。
它可能直接影响:
用户是否敢点击支付。
这就有可能升级成真正的高优先级问题。
三、为什么DOM正确,页面还能错?
这其实又是一道很适合测试开发面试的问题:
接口正确、DOM正确,能不能证明页面一定正确?
答案当然是:
不能。
但为什么?
因为页面最终呈现给用户的不是:
¥860
而是浏览器经过:
HTML
+
CSS
+
字体
+
布局
+
响应式规则
+
组件状态
+
浏览器渲染
最终生成的视觉结果。
所以:
数据正确
≠
DOM正确
≠
渲染正确
≠
用户理解正确
这四层其实是不同的测试对象。
而过去大量自动化测试主要覆盖的是前面两三层。
最后这一层:
用户看到以后会怎么理解?
一直非常依赖人工测试。
这恰恰是多模态模型真正有机会补上的地方。
四、DeepSeek视觉模型真正应该看的,不只是“有没有重叠”
如果我们只是问:
页面有没有元素重叠?
其实还是把多模态模型当成一个高级图像识别器。
更有价值的Prompt应该带上:
业务上下文。
例如把结算页截图交给模型,同时告诉它:
这是电商订单提交页面。
业务规则:
商品原价:1000元
优惠金额:150元
运费:10元
最终应付金额:860元。
请从真实用户支付决策的角度检查截图。
重点判断:
- 最终支付金额是否明确
- 原价、优惠、实付价的视觉层级是否合理
- 是否存在金额重复、遮挡或重叠
- 是否可能导致用户误解实际付款金额
- “提交订单”按钮与支付金额之间是否存在歧义
请输出风险等级及原因。
模型如果识别到异常,可以返回:
{
"passed": false,
"severity": "P1",
"issue_type": "payment_amount_confusion",
"expected": "¥860应为唯一突出展示的实付金额",
"actual": "¥860下方同时出现¥1000",
"risk": "用户可能误认为最终付款金额为1000元"
}
这里就出现了一个非常重要的变化。
过去视觉测试问:
哪里变了?
现在开始问:
这个变化对业务意味着什么?
五、但AI说这是P1,你就真的提P1吗?
还是不能。
这一点特别重要。
多模态模型可以成为“异常发现器”,但不能天然成为最终裁判。
因为模型也可能看错。
所以真正企业级的AI视觉测试,需要建立:
证据链。
模型发现:
“页面存在两个可能的支付金额。”
下一步不是自动创建P1 Bug。
而是让测试系统继续调查。
第一步:确认后端金额到底是多少
import requests
def get_checkout(order_id):
response = requests.get(
f"https://test-api.example.com/orders/{order_id}"
)
response.raise_for_status()
return response.json()
order = get_checkout(
"ORDER_10086"
)
assert order["pay_amount"] == 860
后端:
860
PASS
说明:
金额计算没有问题。
第二步:检查DOM到底出现了几个金额
prices = page.locator(
"[data-testid*='price']"
).all_inner_texts()
print(prices)
得到:
[
"¥1000",
"-¥150",
"¥10",
"¥860",
"¥1000"
]
等等。
正常应该出现4个价格信息。
为什么现在有5个?
于是我们继续找。
price_elements = page.locator(
"[data-testid*='price']"
)
for i in range(price_elements.count()):
item = price_elements.nth(i)
print(
item.inner_text(),
item.get_attribute("data-testid"),
item.bounding_box()
)
发现最后一个:
text:
¥1000
testid:
legacy-original-price
position:
x=1180
y=682
而:
¥860
x=1178
y=670
两个元素几乎叠在一起。
到这里,我们已经基本知道问题在哪了:
旧版价格组件没有正确隐藏。
六、这才是“AI发现Bug”和“AI测试开发”的区别
如果只是:
截图
↓
DeepSeek
↓
发现金额重叠
这只能叫:
AI辅助测试。
但如果继续做到:
截图
↓
视觉模型发现异常
↓
API验证
↓
DOM取证
↓
元素定位
↓
组件归因
↓
缺陷报告
就开始进入真正的:
AI测试开发。
最终系统甚至可以自动生成这样的Bug:
{
"title": "结算页实付金额区域重复展示商品原价",
"severity": "P1",
"order_id": "ORDER_10086",
"expected_pay_amount": 860,
"actual_api_amount": 860,
"visual_issue": "¥1000与¥860在实付区域发生视觉重叠",
"backend_status": "正常",
"suspected_module": "CheckoutPriceSummary",
"suspected_cause": "legacy-original-price组件未正确隐藏",
"evidence": [
"checkout.png",
"api-response.json",
"dom-snapshot.json"
]
}
测试工程师第二天看到的不再只是:
“视觉模型认为页面可能有问题。”
而是:
问题在哪、业务影响是什么、后端是否正常、疑似哪个组件、证据在哪里。
这个价值完全不一样。
七、再往深一点:为什么这种Bug特别适合多模态AI?
因为它同时横跨了三种“正确”。
第一种:数据正确
1000 - 150 + 10 = 860
程序最擅长。
直接Assert。
第二种:功能正确
页面有没有860?
按钮能不能点?
流程能不能提交?
Playwright最擅长。
第三种:语义正确
用户能不能一眼知道:
到底应该支付多少钱?
这件事情传统自动化很难表达。
你当然可以写几十条CSS规则:
assert font_size(pay_amount) > font_size(original_price)
再写:
assert not overlap(
pay_amount,
original_price
)
再写:
assert color_contrast(...)
但是页面复杂以后,规则会越来越多。
而视觉模型真正擅长的恰恰是:
从整体页面理解信息之间的关系。
这才是它应该待的位置。
八、所以未来UI自动化,很可能会出现“双轨验证”
以前:
UI自动化
↓
DOM
↓
业务断言
以后可能逐渐变成:
Playwright
↓
页面真实执行
↓
┌─────────┴─────────┐
↓ ↓
事实验证 视觉验证
↓ ↓
API / DOM / DB Screenshot
↓ ↓
Deterministic Vision Model
↓ ↓
└─────────┬─────────┘
↓
Evaluator
↓
测试结论
左边回答:
系统事实上发生了什么?
右边回答:
用户实际上看到了什么?
最后再把两边合起来。
我认为这比单纯喊:
“AI要替代传统UI自动化。”
靠谱得多。
因为真正成熟的AI测试体系,反而会更依赖传统测试能力。
九、这也是初级测试工程师特别值得理解的一件事
现在很多人学AI测试,路线很容易变成:
Prompt
↓
RAG
↓
Agent
↓
MCP
↓
多模态
这些当然可以学。
但如果面试官突然问:
接口返回正确,为什么页面还能错?
DOM里金额正确,为什么还要做视觉测试?
大模型判断页面有Bug,为什么不能直接作为测试结论?
怎么证明一个视觉Bug到底是后端、前端数据还是CSS渲染问题?
如果这些问题讲不清楚,那么即使知道十个Agent框架,也很难真正做好AI测试开发。
AI时代没有让传统测试知识失效。
反而把一个优秀测试工程师最核心的能力重新放大了:
你到底会不会找证据。
写在最后
DeepSeek视觉模型进入测试以后,我觉得最容易出现的误区就是:
“以后截图扔给AI,让它帮我找Bug。”
这个想法做Demo没问题。
但距离企业级AI测试还差得很远。
真正有价值的链路应该是:
AI发现异常 → 自动化工具验证 → API/DOM/DB取证 → 判断业务影响 → 定位问题 → 输出报告。
AI负责:
发现过去自动化不容易发现的异常。
传统测试工具负责:
证明这个异常到底是不是真的。
而测试工程师负责的,是最重要的那一层:
定义什么才算真正的业务错误。
这也是为什么我一直觉得,多模态模型不会让测试开发的基本功变得不重要。
恰恰相反。
以前你只需要知道:
assert actual == expected
现在你还需要知道:
Expected到底应该从哪里来。
接口?
数据库?
需求?
设计稿?
业务规则?
还是用户最终看到的页面?
当一个测试工程师开始能够把这些东西组合成完整的证据链,他做的就已经不再只是:
“AI帮我看截图。”
而是在构建真正的:
AI视觉质量工程。