UI自动化-POM封装

代码层级:

python 复制代码
项目根目录/
├── conftest.py              # 必选:pytest全局夹具(前置/后置/失败截图)
├── pytest.ini               # 必选:pytest配置文件(自定义用例规则/日志)
├── requirements.txt         # 必选:环境依赖清单(一键还原环境)
├── run.py                   # 必选:用例执行入口(统一运行/生成报告)
├── commons/                 # 必选:通用工具层
│   ├── base_page.py         # 必选:基础数据封装
│   ├── login_page.py        # 必选:页面对象
└── tests/                   # 必选:测试用例层
    └── test_login.py        # 示例:登录测试用例

base_page.py

python 复制代码
import allure
from allure_commons.types import AttachmentType
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC



class BasePage:
    message_xpath = '//p[@class="msg"]'

    def __init__(self,driver):
        self.driver = driver
        self.wait = WebDriverWait(driver,10)
        allure.attach(
            driver.get_screenshot_as_png(),
            attachment_type=AttachmentType.PNG
        )

    @allure.step('元素定位')
    def find_element(self,locator):
        el = self.wait.until(EC.visibility_of_element_located(locator))
        return el

    # 点击事件
    @allure.step('点击按钮')
    def click(self,locator):
        el = self.wait.until(EC.element_to_be_clickable(locator))
        el.click()


    # 输入事件
    @allure.step('输入内容')
    def send_keys(self,locator,text):
        el = self.wait.until((EC.element_to_be_clickable(locator)))
        el.clear()
        el.send_keys(text)

    # 框架切换
    @allure.step('切换iframe')
    def frame(self,url):
        el = self.driver.find_element(By.XPATH,f'//iframe[starts-with(@src,"{url}")]')
        self.driver.switch_to.frame(el)

    # 获取提示语句
    @allure.step('获取提示')
    def get_msg(self):
        el = self.find_element((By.XPATH,self.message_xpath))
        msg = self.wait.until(lambda d: el.text)
        return msg

login_page.py

python 复制代码
import allure
from selenium.webdriver.common.by import By
from commons.base_page import BasePage


class LoginPage(BasePage):
    username_locator = (By.XPATH, '/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/input')
    password_locator = (By.XPATH, '/html/body/div[4]/div/div[2]/div[2]/div/div/div[2]/input')
    login_btn_locator = (By.XPATH, '/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/button')

    @allure.step('进行登录')
    def login(self, username, password):
        self.send_keys(self.username_locator, username)
        self.send_keys(self.password_locator, password)
        self.click(self.login_btn_locator)
        msg = self.get_msg()

        return msg

test_login.py

python 复制代码
import allure
import pytest
from commons.login_page import LoginPage


@allure.epic("用户中心")
@allure.feature("登录功能")
@pytest.mark.parametrize(
    "username,password,_msg",
    [
        ["ces", "111111", "密码错误"],
        ["ces", "1111", "密码格式6~18个字符"],
        ["ces", "123456", "登录成功"],
    ]
)

def test_login(driver,username,password,_msg):
    allure.dynamic.title(_msg)
    driver.get('http://116.62.11/logininfo.html')

    msg = LoginPage(driver).login(username, password)

    assert msg == _msg

conftest.py

python 复制代码
import allure
import pytest
from allure_commons.types import AttachmentType
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


@pytest.fixture()
def driver():
    d = webdriver.Chrome()
    d.maximize_window()
    d.implicitly_wait(2)
    yield d
    d.quit()


@pytest.fixture(scope='session')
def user_driver():
    d = webdriver.Chrome()
    d.maximize_window()
    d.implicitly_wait(2) 
    login_user(d)
    yield d
    d.quit()

def login_user(driver):
    username = 'ces'
    password = '123456'
    success_msg = '登录成功'

    username_input_xpath = '//input[@placeholder="请输入用户名"]'
    password_input_xpath = '//input[@placeholder="请输入密码"]'
    login_button_xpath = '//button[text()="登录"]'
    success_message_xpath = '//p[@class="prompt-msg"]'

    driver.get('http://116.62.1/logininfo.html')
    driver.find_element('xpath', username_input_xpath).send_keys(username)
    driver.find_element('xpath', password_input_xpath).send_keys(password)
    driver.find_element('xpath', login_button_xpath).click()

    success_message = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.XPATH, success_message_xpath))
    )
    assert success_message.text == success_msg

pytest.ini

python 复制代码
[pytest]
# 命令行参数
addopts = -vs  --alluredir=./temps --clean-alluredir  tests/test_login.py
; --reruns 3

markers:
    run

disable_test_id_escaping_and_forfeit_all_rights_to_community_support = true


#日志配置
#日志文件的配置
log_file = ./logs/frame.log
#日志的级别(DEBUG调试,INFO信息,WARNING警告,ERRO错误,CRITICAL非常严重)
log_file_level = info
#日志格式
log_file_format = %(levelname)s|%(asctime)s|%(message)s

requirements.txt

python 复制代码
pytest~=9.0.2
selenium~=4.40.0
pytest-html
pytest-xdist
pytest-order
pytest-rerunfailures
pytest-base_url
allure-pytest
pymysql
pyyaml
allure-python-commons~=2.15.3

run.py

python 复制代码
import shutil
from datetime import datetime
import pytest
import os
import glob

# 执行测试用例
pytest.main()

# 1. 定义日志相关配置(便于维护)
LOG_DIR = "logs"
LOG_PREFIX = "frame_"
LOG_SUFFIX = ".log"
MAX_LOG_NUM = 10  # 最多保留10个日志文件

# 2. 生成新日志文件名并复制
new_file_name = f"{LOG_DIR}/frame_{datetime.now().strftime('%Y%m%d_%H%M%S')}{LOG_SUFFIX}"
shutil.copy(f"{LOG_DIR}/frame.log", new_file_name)


# 3. 清理超出数量的最早日志文件
def clean_old_logs():
    # 筛选出指定格式的日志文件(避免误删其他文件)
    log_files = glob.glob(f"{LOG_DIR}/{LOG_PREFIX}*{LOG_SUFFIX}")
    # 按文件创建时间排序(升序:最早的在前)
    log_files.sort(key=lambda x: os.path.getctime(x))

    # 若文件数量超过阈值,删除最早的
    while len(log_files) > MAX_LOG_NUM:
        oldest_file = log_files.pop(0)  # 取出最早的文件
        try:
            os.remove(oldest_file)
            print(f"删除最早的日志文件:{oldest_file}")
        except Exception as e:
            print(f"删除日志文件失败:{oldest_file},错误:{e}")


# 执行日志清理
clean_old_logs()

# 生成allure报告
os.system("allure generate ./temps -o ./reports --clean")
相关推荐
2401_841495642 小时前
【LeetCode刷题】二叉树的层序遍历
数据结构·python·算法·leetcode·二叉树··队列
roman_日积跬步-终至千里2 小时前
【Java并发】Java 线程池实战:警惕使用CompletableFuture.supplyAsync
java·开发语言·网络
lsx2024062 小时前
C++ 基本的输入输出
开发语言
ZH15455891312 小时前
Flutter for OpenHarmony Python学习助手实战:GUI桌面应用开发的实现
python·学习·flutter
B站计算机毕业设计超人2 小时前
计算机毕业设计Hadoop+Spark+Hive招聘推荐系统 招聘大数据分析 大数据毕业设计(源码+文档+PPT+ 讲解)
大数据·hive·hadoop·python·spark·毕业设计·课程设计
B站计算机毕业设计超人2 小时前
计算机毕业设计hadoop+spark+hive交通拥堵预测 交通流量预测 智慧城市交通大数据 交通客流量分析(源码+LW文档+PPT+讲解视频)
大数据·hive·hadoop·python·spark·毕业设计·课程设计
CodeSheep程序羊2 小时前
拼多多春节加班工资曝光,没几个敢给这个数的。
java·c语言·开发语言·c++·python·程序人生·职场和发展
独好紫罗兰2 小时前
对python的再认识-基于数据结构进行-a002-列表-列表推导式
开发语言·数据结构·python
机器学习之心HML2 小时前
多光伏电站功率预测新思路:当GCN遇见LSTM,解锁时空预测密码,python代码
人工智能·python·lstm