大三测试实习生:我如何让浏览器自己登录 ERP——Playwright UI 自动化

2 阅读8分钟

写在前面:上一篇文章里,我搭完了自己的接口自动化框架(Pytest + 分层 + 数据驱动),当时觉得"自动化测试我算入门了吧",但事实上这才达到门槛,在此之前我就了解过ui自动化,今天,我来教会大家如何搭建ui自动化测试框架。

UI自动化的本质:让代码替我打开浏览器、填表单、点按钮,最后让代码自己判断"到底成没成"。 选的还是那个熟悉的项目——本地部署的开源 ERP(jshERP)。工具是 Playwright。 如果你也和我一样刚学完接口自动化,这篇文章就是为你写的:五关,每一关对应一个真实的坑。


为什么是 Playwright?

选工具时我做了个最简单的调查:Selenium 是ui自动化的老牌大哥,但语法啰嗦、等待机制确实差劲;Playwright 是后起之秀,微软出品,自动等待、智能定位、录制回放都很方便。对新手最友好的一点:它的报错会明明白白告诉你它在找什么、等了多久、没等到什么——Selenium 的报错通常只有一句冰冷的 "NoSuchElementException"。

真正的自动化行业中:UI 自动化在企业里不是必须的,但是得会。自动化测试金字塔里,UI 自动化又贵又脆(页面一改版就挂、跑得慢),所以大厂只拿它测核心链路(登录、下单、审批),10~20 条顶天了。接口自动化才是主力。想清楚这个,你就知道 UI 自动化该学多深、该用在哪。


模块一:使用代码让浏览器打开网页,顺便学会救环境

第一个脚本 11 行,目标是让浏览器自己打开登录页、打印标题、截个图:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:                      # 打开 Playwright 总开关
    browser = p.chromium.launch(headless=False)   # 启动浏览器(False = 能看见窗口)
    page = browser.new_page()                     # 开一个标签页
    page.goto("http://localhost:3000")            # 访问地址
    print("页面标题:", page.title())              # 拿到信息
    page.screenshot(path="登录页.png")            # 截图留证
    browser.close()                               # 关门

跑起来,Chromium 窗口"啪"地弹出来自己打开了登录页——人生第一张程序截的图,那种"代码实现"的感觉真的很奇妙,就是惊讶。


模块二:元素定位——给元素拍身份证

模块二的目标:找到页面上的输入框,把账号密码填进去。核心就两个词:

user_box = page.get_by_placeholder("请输入用户名")   # 定位: 按提示文字找到框
user_box.fill("admin")                               # 动作: 往里填内容

get_by_placeholder 是最直观的定位方式——"定位 = 给元素拍身份证,证件上的每一栏都能当找人线索"。除了提示文字,还有 id、样式类、页面文字、角色……每种线索都有对应的写法。

我踩的第一个坑很离谱:把 goto 的地址改成了 http://google.com,脚本直接全红。问了AI才知道:谷歌在国内连不上(第一层死法);就算连上,谷歌首页也没有"请输入用户名"这个框(第二层死法)。自动化脚本的目标地址,永远是被测系统。

真正让我建立方法论的是三原则:

  1. 唯一性:定位词必须只命中一个元素。我实战撞过车:登录页上有 3 个输入框共用同一个样式类,写 .ant-input 去填,Playwright 直接罢工——"你让我填哪一个?"
  2. 稳定性:挑不容易改版的属性。口诀:id > 提示文字 > 样式类。样式类最容易改(前端换皮肤就变),id 和提示文字是功能性的,改动代价大。
  3. 可读性:三个月后回来看代码,一眼能看懂找的是谁。

模块三:验证码——最难办的问题

登录要四样东西:账号、密码(MD5)、验证码、uuid。前两个好办,难的是验证码:它本来就是用来拦住"不是人"的东西的。

接口课我学会了一招"测试环境后门":jshERP 生成验证码时会把答案写进 Redis,键名是 captcha_codes:{uuid}。问题是——接口课里 uuid 是我自己调接口拿到的,UI 场景里 uuid 在页面的肚子里,我看不见。

答案在 Playwright 的"偷听"能力里:

with page.expect_response(lambda r: "/user/randomImage" in r.url) as ri:
    page.goto("http://localhost:3000")          # 页面加载时, 自己偷偷请求验证码
uuid = ri.value.json()["data"]["uuid"]          # 半路截下回执, 抄走"房间号"
code = read_captcha(uuid)                       # 拿房间号去 Redis 抄答案

翻译成人话:浏览器打开登录页的瞬间,页面 JS 会自己向服务器要一张验证码;我在中间装了个听筒,把它俩的对话截了下来。 拿到 uuid,就能复用接口课的 read_captcha 去 Redis 抄答案。

这是整个学习过程最爽的一次"跨课联动"——接口课造的工具,在 UI 课上复活了。当三个输入框自动填满、登录按钮自动按下、页面"唰"地跳进 ERP 主页时,我真的是比较感慨哈哈哈。

必须声明:这招只适用于测试环境(需要 Redis 权限,生产环境摸不到)。真实项目里,要么让开发在测试环境关掉验证码,要么配万能验证码,要么上 OCR。


模块四:让机器当裁判——expect 断言

第三关确认登录成功的方式很原始:wait_for_timeout(3000) 等待 3 秒 + 我肉眼看截图。

两个问题:等待是赌运气(网快就浪费、网慢就误判);肉眼看图不可规模化(100 条用例看 100 张图,那不叫自动化吗)。

解法是 Playwright 的 expect:

expect(page).to_have_url("http://localhost:3000/dashboard/analysis")   # 断言一1: 地址必须跳走
expect(page.get_by_text("欢迎您,管理员")).to_be_visible()             # 断言2: 欢迎语必须出现

我感觉:这不就是断言嘛。和 Pytest 课的 assert 一个妈生的——条件满足,裁判点头不说话;条件不满足,红字报账。唯一的区别:expect 判的是页面的状态,而且自带智能等待——条件没满足就反复问,最多盯 5 秒,一问到立刻往下走。

那 5 秒的"盯着"就是它比 sleep 高级的地方:sleep 是"睡一觉醒来看一眼",expect 是"盯着门,人一到就开门"。

我还故意打了一针"疫苗":把断言的地址改成一个假的,看看报错并且让AI分析。红字里两样东西很重要——Expected(你要求什么)和 Received(实际是什么)。见过一次失败长什么样,以后红灯亮起的第一反应就是读报账,而不是慌。


第 5 关:接进 Pytest——从基本到流水线

前四关的脚本都靠右键一个个跑,公司里没人这么干,得接进 Pytest 流水线:一条命令跑全部、失败自动报。

我明白:这套流水线的零件我在接口课全学过——conftest.py、fixture、scope,全部平移,一点不带打结的。

# conftest.py
@pytest.fixture(scope="session")
def page(browser):
    """登录一次, 全程复用"""
    ...偷听uuid → Redis抄码 → 填表 → 点登录...
    expect(pg).to_have_url("http://localhost:3000/dashboard/analysis")  # 入场资格
    yield pg

三个设计点(都是接口课迁移过来的思想):

  1. scope="session":整个会话只登录一次,三条用例共享同一个已登录页面——省到极致
  2. fixture 里放断言:登录是"入场资格",连门都进不去,后面全是白跑;死在 fixture 里一眼就知道是"入场环节"挂了
  3. 用例只做"查现场":fixture 负责"到现场",用例负责"查现场"

最终效果:终端敲一条命令,浏览器自动打开 → 登录一次 → 三条用例全绿:

$ python -m pytest test_ui_demo.py -v
test_01_欢迎语可见 PASSED
test_02_侧边菜单齐全 PASSED
test_03_页面标题正确 PASSED

这就是企业回归测试流水线的雏形——以后改代码了,跑一遍就知道系统坏没坏,凌晨三点它也能自己干活。


补充:codegen 录制——最像"作弊器"的工具

AI告诉我一个让我大开眼界的工具。终端敲:

playwright codegen http://localhost:3000

会弹出双胞胎窗口:一个正常浏览器,一个录制面板。你在浏览器里点菜单、敲键盘,代码在另一侧实时长出来——我前四关手敲的每一样东西,它都能自动生成。

但两个专业提醒(不然必翻车):

  1. 录制是草稿生成器,不是成品:生成的定位可能又长又丑甚至撞车,必须过一遍三原则(唯一性/稳定性/可读性)
  2. 录制会把你敲的密码原样写进代码——实际工作中录完必须把密码换成变量或配置读取,明文密码进代码库是安全红线

写在最后

写完回头看,收获远超"会写 UI 自动化":

  • 知识迁移:断言是断言、fixture 是 fixture,接口课和 UI 课殊途同归——所有自动化测试的本质 = 操作 + 断言,操作会变,断言是永恒的灵魂
  • 诚实的边界:Redis 抄验证码只在测试环境合法,UI 自动化在企业里只做尖刀班——知道"什么能做什么不能做",比会写代码更重要

完整代码在 GitHub(包含接口自动化框架):