Playwright页面对象模型POM + 常见断言 + playwright接口断言
- 登录页面Page
-
- 测试用例
- 断言必填提示符
- 断言登录成功或页面跳转成功
- page接口断言
-
- 参数化
登录页面Page
页面原型

页面分析
page对象,核心、核心、核心,一定要传入page对象,一定得记住有传入page对象这回事。
- 用户名输入框
- 密码输入框
- 登录按钮
- 必填提示(用户名和密码)
- 登陆失败提示
代码分析
- 最好不使用xpath语法,而是使用playwright中的属性标签。
- 填充用户名和密码封装成单独的方法
- 点击登录按钮,封装成单独的方法
- 登录流程,将填充用户名和密码操作与点击登录按钮,封装为一个方法
- 清空用户名和密码,填充用户名和密码为空(根据情况进行封装,
若依框架
默认存在用户名和密码)。 - 导航方法,导航到对应的页面。
from playwright.sync_api import Page
class LoginPage:
def __init__(self, page: Page):
self.page = page
self.locator_username = page.locator("//form[@id='signupForm']/input[@class='form-control uname']")
self.locator_password = page.locator("//form[@id='signupForm']/input[@class='form-control pword']")
self.login_button = page.locator("//button[@id='btnSubmit']")
self.username_tips = page.locator("//label[@id='username-error']")
self.password_tips = page.locator("//label[@id='password-error']")
self.login_error_tips = page.get_by_text("用户不存在/密码错误")
self.login_error_tips2 = page.locator("text=用户不存在/密码错误")
self.login_error_tips3 = page.locator("//div[@class='layui-layer-content']")
self.navigate()
def navigate(self):
self.page.goto('/login')
def fill_username(self, username):
self.locator_username.click()
self.locator_username.fill(username)
def fill_password(self, password):
self.locator_password.click()
self.locator_password.fill(password)
def click_login(self):
self.login_button.click()
def login_flow(self, username, password):
self.navigate()
self.fill_username(username)
self.fill_password(password)
self.click_login()
def clean_username_password(self):
self.fill_password('')
self.fill_username('')
测试用例
断言必填提示符
- 使用expect(page).
to_be_visible()
:标签属性可见,红色的提示符从隐藏状态变为可见。 - 使用expect(page).
to_contain_text()
:通过模糊匹配必填提示符文本,进行断言。
import re
import pytest
from playwright.sync_api import Page, expect
from pages.login_page import LoginPage
@pytest.fixture(autouse=True)
def login_page(page):
yield LoginPage(page)
def test_tips(login_page):
"""
断言:登陆失败,必填提示词出现
:param login_page:
:return:
"""
login_page.clean_username_password()
login_page.click_login()
expect(login_page.username_tips).to_be_visible()
expect(login_page.password_tips).to_be_visible()
expect(login_page.username_tips).to_contain_text('用户名')
expect(login_page.password_tips).to_contain_text('请输入您的密码')
def test_login_error(login_page):
"""
断言:登陆失败,用户名密码不正确
:param login_page:
:return:
"""
login_page.login_flow('admin123', 'admin2')
expect(login_page.login_error_tips).to_be_visible()
expect(login_page.login_error_tips2).to_be_visible()
expect(login_page.login_error_tips3).to_be_visible()
断言登录成功或页面跳转成功
- 使用expect(page).
to_have_title()
:模糊匹配页面标题内容,进行断言。 - 使用expect(page).
to_have_url()
:匹配页面url。
def test_login_success(login_page):
"""
断言:登录成功
断言title和url
:param login_page:
:return:
"""
login_page.login_flow('admin', 'admin123')
expect(login_page.page).to_have_title(re.compile('首页'))
expect(login_page.page).to_have_url('/index')
page接口断言
- 接口断言,一般用在前后端分离的项目,登录按钮触发
后端api登录接口
,返回json数据后触发前端代码,跳转到前端对应的页面
。
断言页面跳转
page.expect_navigation()
断言,跳转到的前端地址
def test_login_success2(login_page):
"""
断言:登录成功,显示断言,通过拆分login_flow,判断重定向页面
通过断言页面跳转,来判断是否登录成功
断言title和url
:param login_page:
:return:
"""
login_page.fill_username('admin')
login_page.fill_password('admin123')
with login_page.page.expect_navigation(url='**/index'):
login_page.click_login()
断言请求接口
page.expect_request()
断言,后端请求接口地址
def test_login_success4(login_page):
"""
ajax 断言 request 请求
断言:登录成功,
通过request请求
:param login_page:
:return:
"""
login_page.fill_username('admin')
login_page.fill_password('admin123')
with login_page.page.expect_request('**/login') as req:
login_page.click_login()
assert req.value.method == 'POST', '请求方法不为POST'
assert req.value.headers['x-requested-with'] == 'XMLHttpRequest', '请求协议不为XMLHttpRequest'
assert req.value.post_data_json['password'] == 'admin123', '密码不为admin123'
print(req.value)
<Request url='http://xx.xx.xx.xx/login' method='POST'>
print(req.value.headers)
{'referer': 'http://xx.xx.xx.xx/login', 'x-requested-with': 'XMLHttpRequest','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36', 'accept': '*/*', 'content-type':'application/x-www-form-urlencoded;charset=UTF-8'}
print(req.value.method)
POST
print(req.value.post_data)
username=admin&password=admin123&rememberMe=false
print(req.value.post_data_json)
{'username': 'admin', 'password': 'admin123', 'rememberMe': 'false'}
print(req.value.post_data_buffer)
b'username=admin&password=admin123&rememberMe=false'
断言响应接口
page.expect_response()
断言,根据接口请求响应,断言状态码等
def test_login_success5(login_page):
"""
ajax 断言 响应 response
断言:登录成功,显示断言,
断言response 返回
:param login_page:
:return:a
"""
login_page.fill_username('admin')
login_page.fill_password('admin123')
with login_page.page.expect_response('**/login') as res:
login_page.click_login()
assert res.value.status == 200
assert res.value.url.__contains__('login')
assert res.value.headers['content-type'] == 'application/json'
assert res.value.ok.__eq__(True)
print(res.value)
print(res.value.status)
print(res.value.url)
print(res.value.ok)
print(res.value.headers)
- 重点
- .json()、.text()、.body():他们都是获取当前页面page的信息,但是我们登录成功后,会重定向到/index页面,因此获取不到该字段信息
他们需要用在专门的后端接口
。
参数化
- playwright中使用参数化与pytest中基本一致
- 注意:参数化时,最好正例在一起,反例在一起,正例反例不要混在一起写
@pytest.mark.parametrize('username, password', [
('admin', 'admin123'),
('test_0001', 'admin123')
])
def test_login_success5(login_page, username, password):
"""
ajax 断言 响应 response
断言:登录成功,显示断言,
断言response 返回
:param login_page:
:return:a
"""
login_page.fill_username(username)
login_page.fill_password(password)
with login_page.page.expect_response('**/login') as res:
login_page.click_login()
assert res.value.status == 200
assert res.value.url.__contains__('login')
assert res.value.headers['content-type'] == 'application/json'
assert res.value.ok.__eq__(True)