fixture

fixture 作用域

1
2
3
4
5
6
7
8
9
10
11
1. 默认为function,每次调用都会执行一次/新建一次

2. class,类中定义的fixture,在类中复用

3. module,同一个py文件中复用

4. package,同一个目录下的py文件中复用

5. session,整个项目复用
例子:
@fixture(scope="module")

yield 的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
yieldreturn的增强版

1. 将数据传递后再处理
@pytest.fixture
def basic_data():
data = {"value": 42}
yield data
#后置处理
data = {"value": 43}

def test_value(basic_data):
assert basic_data["value"] == 42


2. 一个函数中只能有一个yield
错误示范:
def multi_phase_context():
print("初始化阶段")
yield "第一阶段数据"

print("中间处理阶段")
yield "第二阶段数据" # 注意:这仍然会在 pytest fixture 中报错

print("清理阶段")

# 在普通代码中可以这样用(但不在 pytest fixture 中)
def normal_function():
with multi_phase_context() as phase1:
print(f"使用 {phase1}")

外置化参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

1 . config.py 配置类
@pytest.fixture
def app_config(request):
"""最简单的配置读取 fixture"""
# 从测试模块读取配置,如果没有就使用默认值
app_name = getattr(request.module, "APP_NAME", "默认应用")
timeout = getattr(request.module, "TIMEOUT", 30)

return {
"app_name": app_name,
"timeout": timeout
}

2. test_simple.py 测试文件
# 模块级别的配置
APP_NAME = "我的应用"
TIMEOUT = 60

def test_basic_config(app_config):
"""测试基本配置读取"""
assert app_config["app_name"] == "我的应用"
assert app_config["timeout"] == 60

def test_default_values():
"""测试没有配置时的默认值(需要新模块)"""
# 这个测试会使用默认值,因为当前模块有配置
# 要测试默认值,需要创建新模块
pass

获取标记的数据

1
2
3
4
5
6
7
8
9
10
11
12
@pytest.fixture
def simple_test(request):
marker = request.node.get_closest_marker("base_config")
base = marker.args[0]
add = getattr(request,"param",1)
return base + add

@pytest.mark.base_config(10) # 基础值
@pytest.mark.parametrize("simple_test", [1, 2, 3], indirect=True)
def test_file_operations(simple_test):
print(simple_test)
assert simple_test in [11,12,13]

工厂模式fixture

1
2
3
4
5
6
7
8
9
10
11
12
@pytest.fixture
def make_customer_record():
def _make_customer_record(name):
return {"name": name, "orders": []}

return _make_customer_record


def test_customer_records(make_customer_record):
customer_1 = make_customer_record("Lisa")
customer_2 = make_customer_record("Mike")
customer_3 = make_customer_record("Meredith")

参数化fixture

  1. 简单参数化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import pytest
    @pytest.fixture(params=[
    # 字典列表
    {"name": "Alice", "age": 30, "role": "admin"},
    {"name": "Bob", "age": 25, "role": "user"},
    {"name": "Charlie", "age": 35, "role": "moderator"},
    ])
    def user_data(request):
    return request.param

    def test_user_info(user_data):
    assert "name" in user_data
    assert user_data["age"] > 0
    assert user_data["role"] in ["admin", "user", "moderator"]
    print(f"测试用户: {user_data['name']} ({user_data['role']})")
  1. 类中参数化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class User:
    # 魔术方法,初始化时调用
    def __init__(self, name, email, is_active=True):
    self.name = name
    self.email = email
    self.is_active = is_active
    # 魔术方法,打印对象时调用
    def __repr__(self):
    return f"User({self.name}, {self.email}, active={self.is_active})"


    # 在这个注解就以及创建了实例
    @pytest.fixture(params=[
    User("admin", "admin@example.com"),
    User("user", "user@example.com", False),
    User("guest", "guest@example.com"),
    ])
    def user_instance(request):
    # 返回参数
    return request.param

    def test_user_instance(user_instance):
    assert "@" in user_instance.email
    assert len(user_instance.name) > 0
    # python参数化会使每个参数都执行一次
    print(f"测试用户实例: {user_instance}")
  1. 动态生成参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def generate_test_data():
    """动态生成测试数据"""
    return [
    (i, i * 2, f"数据{i}") for i in range(1, 4)
    ]

    @pytest.fixture(params=generate_test_data())
    def dynamic_data(request):
    input_val, expected, description = request.param
    return {
    "input": input_val,
    "expected": expected,
    "desc": description
    }

    def test_dynamic_data(dynamic_data):
    result = dynamic_data["input"] * 2
    assert result == dynamic_data["expected"]
    print(f"{dynamic_data['desc']}: {dynamic_data['input']} * 2 = {result}")
  1. 带id的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@pytest.fixture(params=[
{"browser": "chrome", "headless": True},
{"browser": "firefox", "headless": True},
{"browser": "safari", "headless": False},
], ids=["chrome_headless", "firefox_headless", "safari_visible"])
def browser_config(request):
config = request.param
# 模拟浏览器配置
return {
"browser": config["browser"],
"headless": config["headless"],
"user_agent": f"Mozilla/5.0 ({config['browser']})"
}

def test_browser_compatibility(browser_config):
print(f"测试浏览器: {browser_config['browser']} (无头模式: {browser_config['headless']})")
# 这里可以实际初始化浏览器进行测试
assert browser_config["browser"] in ["chrome", "firefox", "safari"]

selenium

1

  1. 动态生成参数
1

  1. 动态生成参数
1

  1. 动态生成参数
1

  1. 动态生成参数
1

  1. 动态生成参数
1

  1. 动态生成参数
1