You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pytest BDD中Fixture与Conftest问题:Feature参数无法识别

问题解决:pytest-bdd 中「fixture 'stage' not found」错误修复

错误原因

你将@pytest.fixture与@given装饰器混用在同一个函数上,导致pytest把步骤解析出来的stage、tenant参数当成pytest fixture去查找,而这两个参数是来自Gherkin特征文件的动态参数,并非预定义的fixture,因此触发找不到fixture的错误。

分步修复方案

1. 修正 conftest.py 中的认证步骤

移除http_session函数上的@pytest.fixture装饰器,改用target_fixture参数将步骤返回的会话对象转为fixture,供后续步骤复用:

import pytest
import requests
import json  # 补充缺失的json导入
from pytest_bdd import given, parsers

@pytest.fixture(scope='session')
def config():
    with open('config.json') as config_file:
        config = json.load(config_file)
    return config

# 用target_fixture将步骤返回值转为可复用的fixture
@given(parsers.parse('The user is authenticated on stage "{stage}" with tenant "{tenant}"'), target_fixture="http_session")
def http_session(config, stage, tenant):
    login_url = f"https://{stage}/{tenant}/public/login"
    payload = {
       'username': config['username'],
       'password': config['password']
    }
    session = requests.Session()
    response = session.post(login_url, data=payload, allow_redirects=True)
    
    # 新增登录成功断言,提前排查认证问题
    assert response.status_code in (200, 302), f"认证失败,状态码:{response.status_code}"
    return session

2. 新增请求参数存储 fixture(避免测试污染)

在conftest.py中添加request_data fixture,用于存储每个测试用例的临时请求参数,避免修改全局config字典导致的测试污染:

@pytest.fixture(scope='function')
def request_data(config):
    return {
        'url': config['url'],
        'http_type': None,
        'headers': config['headers']
    }

3. 修正 test_apis.py 中的步骤定义

替换原有的全局config修改逻辑,改用request_data存储临时参数,并修复断言错误:

from pytest_bdd import scenarios, given, when, then, parsers
import pytest

scenarios('../features/api.feature')

@given(parsers.parse('I set the request to endpoint "{endpoint}"'))
def set_endpoint(request_data, endpoint):
    assert len(endpoint) > 0, "endpoint参数不能为空"
    request_data['url'] = request_data['url'] + endpoint

@when(parsers.parse('I send a HTTP "{http_type}" request'))
def set_http_type(request_data, http_type):
    valid_methods = ('get', 'post')
    assert http_type.lower() in valid_methods, f"不支持的HTTP方法:{http_type}"
    request_data['http_type'] = http_type.lower()

@then(parsers.parse('I expect http response with status code "{status_code}" and response type "{response_type}"'))
def request_endpoint_statuscode(request_data, http_session, status_code, response_type):
    assert status_code.isdigit(), "状态码必须为数字格式"
    valid_types = ('json', 'xml')
    assert response_type.lower() in valid_types, f"不支持的响应类型:{response_type}"
    
    url = request_data['url']
    http_type = request_data['http_type']
    headers = request_data['headers']
    
    # 发送请求
    if http_type == 'get':
        response = http_session.get(url, headers=headers, allow_redirects=True)
    elif http_type == 'post':
        response = http_session.post(url, headers=headers, allow_redirects=True)
    else:
        pytest.fail(f"未实现的HTTP方法:{http_type}")
    
    # 断言状态码
    assert response.status_code == int(status_code), \
        f"期望状态码:{status_code},实际状态码:{response.status_code}"
    
    # 断言响应类型
    assert response_type.lower() in response.headers.get('Content-Type', ''), \
        f"期望响应类型:{response_type},实际Content-Type:{response.headers.get('Content-Type')}"

4. 特征文件参数替换

将api.feature中的占位符(如var_stage、var_tenant)替换为实际的环境值,例如:

Background:
    Given The user is authenticated on stage "dev.example.com" with tenant "test_tenant"

Scenario: Basic Api request
    Given I set the request to endpoint "/api/v1/manuals"
    When I send a HTTP "get" request
    Then I expect http response with status code "200" and response type "json"

关键说明

  • 不要混用@pytest.fixture和@given:pytest-bdd的步骤函数参数来自Gherkin解析,并非pytest fixture,两者不能直接混用。
  • 用target_fixture实现步骤返回值复用:通过该参数可以将步骤函数的返回值转为fixture,供后续步骤依赖注入。
  • 避免全局状态修改:使用独立的fixture存储测试临时参数,防止不同测试用例之间的状态污染。

内容的提问来源于stack exchange,提问作者s kop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 22:45:01