当前位置: 首页 > news >正文

Playwright页面对象模型POM + 常见断言 + playwright接口断言 + 参数化

Playwright页面对象模型POM + 常见断言 + playwright接口断言

    • 登录页面Page
      • 页面原型
      • 页面分析
      • 代码分析
    • 测试用例
      • 断言必填提示符
      • 断言登录成功或页面跳转成功
      • page接口断言
        • 断言页面跳转
        • 断言请求接口
        • 断言响应接口
      • 参数化

登录页面Page

页面原型

在这里插入图片描述

页面分析

  • page对象,核心、核心、核心,一定要传入page对象,一定得记住有传入page对象这回事。
  • 用户名输入框
  • 密码输入框
  • 登录按钮
  • 必填提示(用户名和密码)
  • 登陆失败提示

代码分析

  • 最好不使用xpath语法,而是使用playwright中的属性标签。
  • 填充用户名和密码封装成单独的方法
  • 点击登录按钮,封装成单独的方法
  • 登录流程,将填充用户名和密码操作与点击登录按钮,封装为一个方法
  • 清空用户名和密码,填充用户名和密码为空(根据情况进行封装,若依框架默认存在用户名和密码)。
  • 导航方法,导航到对应的页面。
from playwright.sync_api import Page


class LoginPage:
    def __init__(self, page: Page):
    	# 必须传入page对象,断言时会用到page页面。
        self.page = page
        # 使用xpath语法获取用户名文本框
        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()
        # 等待页面跳转,作为登录成功的标准
        # self.page.wait_for_url(url='/index')

    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('请输入您的密码')
    # 若依登录按钮,可以点击,只是无法生效,该断言报错。
    # expect(login_page.login_button).not_to_be_enabled()


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'
  • req.value对象的值
    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)
  • response.value对象的值

    print(res.value)
    print(res.value.status)
    # 200

    print(res.value.url)
    # http://xx.xx.xx.xx/login

    print(res.value.ok)
    # True

    print(res.value.headers)
    # {'transfer-encoding': 'chunked', 'keep-alive': 'timeout=60', 'date': 'Sun, 30 Mar 2025 06:04:44 GMT', 
    # 'content-type': 'application/json', 'connection': 'keep-alive'} 
  • 重点
  • .json()、.text()、.body():他们都是获取当前页面page的信息,但是我们登录成功后,会重定向到/index页面,因此获取不到该字段信息
  • 他们需要用在专门的后端接口
    # .json()、.text()、.body():他们都是获取当前页面page的信息,但是我们登录成功后,会重定向到/index页面,因此获取不到该字段信息
    # print(res.value.json())
    # print(res.value.text())
    # print(res.value.body())

参数化

  • 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)

相关文章:

  • 深入理解 DNS:互联网的“电话簿”
  • 【DPI】精讲深度数据包检测技术基础
  • Oracle数据库数据编程SQL<3.3 PL/SQL 游标>
  • 关于中文编程的一些思考
  • 使用 Cheerio 和 Node.js 进行网络抓取
  • 数据结构初阶:单链表
  • golang strings包常用方法
  • [MySQL] 库的操作 表的操作
  • Unity光线传播体积(LPV)技术实现详解
  • 基于Java(SSM)+Mysql实现移动大厅业务办理(增删改查)
  • 行业智能体大爆发,分布式智能云有解
  • 视觉风格提示词:Visual Style Prompting with Swapping Self-Attention(风格迁移)
  • Dubbo(24)如何配置Dubbo的监控中心?
  • 深度学习之卷积
  • 【零基础入门unity游戏开发——2D篇】2D物理系统 —— 2DEffector效应器
  • Rust 所有权与引用
  • 要创建一个基于Spring Boot、Thymeleaf、MyBatis Plus和MySQL的简单表格增删改查(CRUD)项目
  • RabbitMQ--延迟队列事务消息分发
  • 全面守护关键基础设施!Fortinet OT 安全平台功能与服务再扩展
  • Level DB --- TableCache
  • 企业网站seo多少钱/网站建设设计
  • 好的买手表网站/合肥seo服务商
  • 淄博哪家网络公司做网站好/中国舆情网
  • 选择常州网站建设公司/杭州seo排名
  • 设计师网站源码/网络营销推广策划方案
  • 网络公司怎么优化网站/开发一个app需要多少钱