第三章里,我们跑出了第一个 1 passed。
浏览器打开了,标题也验证了,看起来已经会写自动化了。
但如果现在让你测试登录功能,很多人会立刻把操作从头写到尾,打开页面、输入账号、输入密码、点击登录,然后结束。
问题就在这里。
按钮点完了,不代表测试完成了。
这一章我们把一条自动化测试拆开,看清它究竟由什么组成。
01 测试不是操作清单
一条完整测试至少包含四部分。
前置条件,测试开始前系统必须处于什么状态。
操作步骤,用户具体做了什么。
预期结果,正确系统应该出现什么变化。
清理动作,测试产生的数据是否需要恢复。
以登录为例,前置条件是账号存在且处于启用状态。操作是输入账号密码并点击登录。预期结果可能是 URL 进入首页、页面显示用户名、服务端返回登录成功。清理动作则可能是退出登录或清除状态。
如果只有操作,没有预期结果,它只是浏览器自动操作。
02 Arrange、Act、Assert
测试领域经常使用 AAA 结构。
Arrange 负责准备,Act 负责执行,Assert 负责验证。
from playwright.sync_api import Page, expectdef test_search(page: Page): # Arrange page.goto("https://www.baidu.com") # Act page.get_by_id("kw").fill("Playwright Python") page.get_by_id("su").click() # Assert expect(page).to_have_title(lambda title: "Playwright" in title)真实项目不一定非要保留这三行注释,但写代码时脑子里要有这个结构。
准备和验证混在操作中间,测试会越来越难读。
03 应该断言什么
好的断言要贴近业务结果。
点击登录后,只验证按钮消失通常不够。按钮消失可能是页面卡住,也可能是前端重新渲染。
更有价值的验证包括。
URL 是否进入首页 用户昵称是否出现 登录后的导航是否可见 关键接口是否成功 登录状态是否被保存
同一条测试也不是断言越多越好。断言应该围绕测试标题服务。
如果测试标题是“正确账号可以登录”,验证登录成功即可。订单数量、头像样式和公告内容不属于这条测试,不要顺手全检查。
04 一条测试只验证一个核心目标
把注册、登录、修改资料、退出登录全部塞进一条用例,执行起来很省事,失败时却很痛苦。
它可能在任何一步断掉,而报告只会告诉你这条超长用例失败了。
更清楚的做法是拆分。
def test_login_with_valid_account(page: Page): ...def test_login_with_wrong_password(page: Page): ...def test_logout(page: Page): ...每条测试拥有一个明确问题。失败时,不打开代码也能大致知道哪里出了问题。
05 测试之间不能相互依赖
不要让第二条测试必须等第一条创建数据后才能运行。
pytest 可能调整顺序,也可能并行执行。前一条失败,后面一串用例会跟着倒下。
需要用户数据,就在当前测试的前置条件中创建,或者通过 fixture 和接口准备。用完以后清理。
测试应该像独立房间,而不是一串多米诺骨牌。
06 失败信息也是设计的一部分
比较下面两个断言。
assert page.locator(".message").count() > 0expect(page.get_by_text("登录成功")).to_be_visible()第二种写法不仅会自动等待,失败信息也更接近我们真正关心的问题。
以后写断言时,多问一句,如果它失败了,半夜看报告的人能不能立刻理解?
07 本章验收
[ ] 能说出前置条件、操作、预期结果和清理动作 [ ] 能用 Arrange、Act、Assert 拆解测试 [ ] 知道操作成功不等于测试通过 [ ] 一条测试只围绕一个核心目标 [ ] 测试之间不依赖执行顺序
下一章开始补 Python 基础。
不是从语法大全开始,而是从测试里每天都会出现的变量开始。变量、字符串和数字