一、父类页面BasePage

先接上集的POM设计方法,由于封装的重写定位元素的方法,以及触发显示等待在所有页面类中都可以使用,但所有页面类都需要重新复制优化后的封装代码。 

为了让代码清晰简洁减少重复,使用继承的方式让每一个子页面类获取父页面类的方法和属性。

所有的页面类都需要获取:

  • 实例属性:驱动
  • 实例方法:显示等待,定位元素

BasePage一般在POM模式中当作所有页面的父类(元类)

BasePage一般属于抽象类的范畴,不建议直接实例化其实例对象,不能代表任何的实例对象。

把子类页面中共同需要使用的属性或者方法统一封装到父类中:

类属性,类方法,实例属性,实例方法。

 方便调用实现代码的高度复用(将厕所放到公共区域而不是你的卧室 大家都方便去上厕所)

 二、pytest封装

1.函数用例为主 格式def test_xxx() 

2.执行某个特定的用例:pytest -sk test_xxx 

三、Xpath为动态元素的值时总是会改变

此时不能通过简单的复制,要自己手写Xpath:

四、XPath 手写方法指南

XPath是一种用于在 XML/HTML 文档中定位元素的语言,在自动化测试中广泛使用。

1. 基本语法结构

XPath 使用路径表达式来选择节点,基本格式为:

/  - 根节点
// - 任意层级节点
.  - 当前节点
.. - 父节点
@  - 属性
2. 绝对路径与相对路径
  • 绝对路径:从根节点开始的完整路径(慎用,页面结构变化时易失效)

    /html/body/div[1]/div[2]/form/input
    
  • 相对路径:从当前节点开始(推荐)

    //div[@class="container"]//input
    
3. 按元素名称定位
//div         # 所有div元素
//input       # 所有input元素
//button      # 所有button元素
4. 按属性定位
//input[@id="username"]         # id属性等于"username"的input
//a[@href="https://example.com"] # href属性等于指定值的a标签
//div[@class="content"]          # class属性等于"content"的div
5. 按文本内容定位
//button[text()="登录"]           # 文本完全等于"登录"的button
//a[contains(text(), "注册")]      # 文本包含"注册"的a标签
//p[starts-with(text(), "欢迎")]   # 文本以"欢迎"开头的p标签
6. 组合条件(逻辑运算符)
//input[@type="text" and @name="email"]  # 同时满足两个条件
//button[@id="submit" or @class="btn"]   # 满足任一条件
//div[not(@class="disabled")]            # 不满足条件
7. 层级关系定位
//div[@id="parent"]/child::span          # 直接子节点span
//div[@id="parent"]//descendant::span    # 任意层级子节点span
//span[@id="child"]/parent::div          # 父节点div
//input[@id="username"]/following-sibling::button  # 下一个兄弟节点button
//button[@id="submit"]/preceding-sibling::input    # 前一个兄弟节点input
8. 索引定位(从 1 开始)
//div[@class="item"][1]         # 第一个匹配的div
//ul/li[last()]                 # 最后一个li
//ul/li[position() < 3]         # 前两个li
9. 模糊匹配
//input[contains(@id, "user")]  # id包含"user"的input
//div[starts-with(@class, "box")] # class以"box"开头的div
//a[ends-with(@href, ".pdf")]   # href以".pdf"结尾的a标签(需XPath 2.0支持)
10. 轴(Axes)定位
//div[@id="content"]/ancestor::body      # 所有祖先节点(直到根节点)
//input[@id="email"]/following::button   # 当前节点之后的所有button
//button[@id="save"]/preceding::input    # 当前节点之前的所有input
11. 使用函数
//input[count(//div) > 5]                # 满足条件的input(div数量>5)
//div[contains(@class, "active") and string-length(text()) > 10]  # 文本长度>10的div

12. 复杂示例
//form[@id="loginForm"]//input[@name="username" and @type="text"]
//table[@class="data"]/tbody/tr[position() mod 2 = 0]  # 表格偶数行
//div[contains(@class, "product") and .//span[text()="热门"]]  # 包含"热门"标签的产品

五、Fixture 夹具:自动化测试中的「前置准备」与「后置清理」

在自动化测试中,Fixture(夹具)是一种特殊的函数或机制,用于为测试用例提供一致的、可重复的测试环境。它就像是实验室中的实验设备,确保每次实验(测试)都在相同条件下进行。

1. 为什么需要 Fixture?

  • 避免重复代码:测试前的初始化(如登录、创建数据)和测试后的清理(如删除数据、关闭连接)逻辑,无需在每个测试用例中重复编写。
  • 保证测试独立性:每个测试用例运行前都有干净的环境,避免测试之间相互干扰。
  • 提高可维护性:环境配置集中管理,修改时只需更新 Fixture,无需修改所有测试用例。

2. pytest框架中的 Fixture 实现

  • Python(pytest 框架)

pytest 使用@pytest.fixture装饰器定义 Fixture。

import pytest

# 定义Fixture
@pytest.fixture
def setup_user():
    # 前置操作:创建测试用户
    user = {"name": "test", "id": 123}
    yield user  # 返回数据给测试用例
    # 后置操作:清理用户(yield之后的代码)

# 使用Fixture
def test_user_profile(setup_user):
    assert setup_user["name"] == "test"

带参数的 Fixture

@pytest.fixture(params=["chrome", "firefox"])
def browser(request):
    # 根据参数初始化不同浏览器
    if request.param == "chrome":
        driver = webdriver.Chrome()
    else:
        driver = webdriver.Firefox()
    yield driver
    driver.quit()  # 关闭浏览器

    3. Fixture 的高级用法

    (1)共享 Fixture(pytest)

    在conftest.py文件中定义 Fixture,使其可被多个测试文件共享。

    # conftest.py
    @pytest.fixture(scope="session")  # 整个测试会话只执行一次
    def database_connection():
        conn = create_db_connection()
        yield conn
        conn.close()
    

    (2)Fixture 嵌套

    Fixture 可以依赖其他 Fixture。

    @pytest.fixture
    def login_user(database_connection):
        # 使用database_connection Fixture
        user = create_user_in_db(database_connection)
        token = login(user)
        yield token
        delete_user(database_connection, user.id)
    

    (3)参数化 Fixture

    通过参数化生成多组测试数据。

    @pytest.fixture(params=[1, 2, 3])
    def numbers(request):
        return request.param
    
    def test_numbers(numbers):
        assert numbers > 0
    

    4. Fixture 的作用域(Scope)

    Fixture 的作用域决定了它的执行频率,常见作用域包括:

    • function(默认):每个测试函数执行一次。
    • class:每个测试类执行一次。
    • module:每个测试模块执行一次。
    • session:整个测试会话执行一次(如数据库连接)。
    @pytest.fixture(scope="class")
    def shared_resource():
        # 类级别的Fixture
        resource = setup_resource()
        yield resource
        cleanup(resource)
    

    5. 最佳实践

    1. 单一职责:Fixture 应专注于环境准备,避免包含业务逻辑。
    2. 命名清晰:使用描述性名称(如setup_database、login_admin)。
    3. 避免过度依赖:Fixture 之间的依赖应尽量简单,避免复杂的调用链。
    4. 适当的清理:确保 Fixture 执行后环境恢复原状,避免测试污染。

    核心:分离测试逻辑与环境管理。

    # web自动化 

    更多推荐