英文:
Pytest BDD - Problem with Fixtures and Conftest
问题
I can help translate the code and content you provided. Here's the translated content:
我正在尝试使用pytest-bdd和gherkin特性文件来自动化API测试。
我的文件夹结构如下:
C:.
│   config.json
│   conftest.py
│   geckodriver.log
│   __init__.py
├───.vscode
│       settings.json
│
├───features
│       api.feature
│
├───step_defs
│   │   test_apis.py
│   │   __init__.py
我有以下特性文件:
api.feature
Feature: API测试
作为用户,
我希望从外部API获得反馈,
以便找到我的正确用户手册。
Background:
    Given 用户在阶段 "var_stage" 上已通过租户 "var_tenant" 进行了身份验证
Scenario: 基本API请求
    Given 我将请求设置为端点 "var_endpoint"
    When 我发送HTTP "get"请求
    Then 我期望HTTP响应的状态码为 "var_status_code" 且响应类型为 "json"
我想测试不同的API,但要确保在执行不同的场景之前(参见Background)只进行一次必要的身份验证。出于这个原因,我已经在conftest.py文件中使用用户会话为特性文件中指定的阶段和租户设置了身份验证,以及一个装置。
```python
conftest.py
import pytest
import requests
from pytest_bdd import given, parsers
@pytest.fixture
def config(scope='session'):
    # 读取文件
    with open('config.json') as config_file:
        config = json.load(config_file)
    # 返回配置以供使用
    return config
@pytest.fixture
@given(parsers.parse('用户已通过阶段 "{stage}" 和租户 "{tenant}" 进行了身份验证'))
def http_session(config, stage, tenant, scope='session'):
    login_url = "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)
我的另一个文件看起来像这样:
test_apis.py
from pytest_bdd import scenarios, given, when, then, parsers
scenarios('../features/api.feature')
@given(parsers.parse('我将请求设置为端点 "{endpoint}"'))
def set_endpoint(config, endpoint):
    assert len(endpoint) > 0
    url = config['url'] + \
        endpoint
    config['url'] = url
@when(parsers.parse('我发送HTTP "{http_type}"请求'))
def set_http_type(config, http_type):
    assert len(http_type) > 0
    config['http_type'] = http_type
@then(parsers.parse('我期望HTTP响应的状态码为 "{status_code}" 且响应类型为 "{response_type}"'))
def request_endpoint_statuscode(config, http_session,  status_code, response_type):
    assert len(status_code) > 0
    assert len(response_type) > 0
    
    headers = config['headers']
    url = config['url']
    http_type = config['http_type']
    status_code_respone = None
    if http_type == 'get':
        status_code_response = http_session.get(
            url, headers=headers, allow_redirects=True)
    elif http_type == 'post':
        status_code_response = http_session.post(
            url, headers=headers, allow_redirects=True)
    assert status_code_response == int(status_code)
运行时,我收到以下错误:
@pytest.fixture
  @given(parsers.parse('用户已通过阶段 "{stage}" 和租户 "{tenant}" 进行了身份验证'))
  def http_session(config, stage, tenant, scope='session'):
E       fixture 'stage' not found
>       available fixtures: _pytest_bdd_example, browser, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, config, doctest_namespace, http_session, monkeypatch, pytestbdd_stepdef_given_I set brandcode to "{brandcode}", pytestbdd_stepdef_given_I set the request to endpoint "{endpoint}", pytestbdd_stepdef_given_The user is authenticated on stage "{stage}" with tenant "{tenant}", pytestbdd_stepdef_given_trace, pytestbdd_stepdef_then_I expect HTTP response with status code "{status_code}" and response type "{response_type}", pytestbdd_stepdef_then_response content length shall be greater than "{length}", pytestbdd_stepdef_then_trace, pytestbdd_stepdef_when_I send a HTTP "{http_type}" request, pytestbdd_stepdef_when_trace, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
>       use 'pytest --fixtures [testpath]' for help on them.
我猜这与特性文件以及如何与pytest.fixture一起使用有关。只使用配置文件而不使用pytest-bdd在conftest中可以正常工作,用于创建HTTP会话。但是,我希望能够在特性文件中指定阶段和租户(可能还包括测试用户的凭据)。
<details>
<summary>英文:</summary>
I'm trying to automatize API testing with pytest-bdd and gherkin feature files. 
 
My folder structur looks like this 
    C:.
    │   config.json
    │   conftest.py
    │   geckodriver.log
    │   __init__.py
    ├───.vscode
    │       settings.json
    │
    ├───features
    │       api.feature
    │
    ├───step_defs
    │   │   test_apis.py
    │   │   __init__.py
     
   
I have following feature file:
    api.feature
    Feature: API Tests
    As a User,
    I want to have feedback from external APIs,
    So that my correct user manual can be found.
        
    Background:
        Given The user is authenticated on stage "var_stage" with tenant "var_tenant"
    Scenario: Basic Api request
        Given I set the request to endpoint "var_endpoint"
        When I send a HTTP "get" request
        Then I expect http response with status code "var_status_code" and response type "json"
I want to test different APIs but want to ensure that the necessary authentication is done once before (see Brackground) different scenarios are executed. For this reason I have setup the authentication using session of the user for the stage and tenant specified in the feature file in conftest.py together with a fixture. 
```python
    conftest.py
    import pytest
    import requests
    from pytest_bdd import given, parsers
    
    
    @pytest.fixture
    def config(scope='session'):
    
        # Read the file
        with open('config.json') as config_file:
            config = json.load(config_file)
    
        # Return config so it can be used
        return config
    @pytest.fixture
    @given(parsers.parse('The user is authenticated on stage "{stage}" with tenant "{tenant}"'))
    def http_session(config, stage, tenant, scope = 'session'):
    
        login_url = "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)
My other file looks like this:
    test_apis.py
    
    from pytest_bdd import scenarios, given, when, then, parsers
    
    scenarios('../features/api.feature')
    
    @given(parsers.parse('I set the request to endpoint "{endpoint}"'))
    def set_endpoint(config, endpoint):
    
        assert len(endpoint) > 0
    
        url = config['url'] + \
            endpoint
        config['url'] = url
    
    
    @when(parsers.parse('I send a HTTP "{http_type}" request'))
    def set_http_type(config, http_type):
    
        assert len(http_type) > 0
    
        config['http_type'] = http_type
    
    
    @then(parsers.parse('I expect http response with status code "{status_code}" and response type "{response_type}"'))
    def request_endpoint_statuscode(config, http_session,  status_code, response_type):
    
        assert len(status_code) > 0
        assert len(response_type) > 0
        
        headers = config['headers']
        url = config['url']
        http_type = config['http_type']
        status_code_respone = None
    
        if http_type == 'get':
            status_code_response = http_session.get(
                url, headers=headers, allow_redirects=True)
    
        elif http_type == 'post':
            status_code_response = http_session.post(
                url, headers=headers, allow_redirects=True)
    
        assert status_code_response == int(status_code)
Running this I receive following error
    @pytest.fixture
      @given(parsers.parse('The user is authenticated on stage "{stage}" with tenant "{tenant}"'))
      def http_session(config, stage, tenant, scope='session'):
    E       fixture 'stage' not found
    >       available fixtures: _pytest_bdd_example, browser, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, config, doctest_namespace, http_session, monkeypatch, pytestbdd_stepdef_given_I set brandcode to "{brandcode}", pytestbdd_stepdef_given_I set the request to endpoint "{endpoint}", pytestbdd_stepdef_given_The user is authenticated on stage "{stage}" with tenant "{tenant}", pytestbdd_stepdef_given_trace, pytestbdd_stepdef_then_I expect http response with status code "{status_code}" and response type "{response_type}", pytestbdd_stepdef_then_response content length shall be greater than "{length}", pytestbdd_stepdef_then_trace, pytestbdd_stepdef_when_I send a HTTP "{http_type}" request, pytestbdd_stepdef_when_trace, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
    >       use 'pytest --fixtures [testpath]' for help on them.
I guess that it has something to do with the feature file and how it is used together with pytest.fixtures. Using only configuration file without pytest-bdd in conftest worked fine for creation of the http session. However I want to be able to specify the stage and tenant (and possibly also credentials for a test user) within the feature file.
答案1
得分: 1
错误发生是因为 pytest 尝试将 http_session 作为一个 fixtures 运行,但在 pytest-bdd 执行步骤 Given The user is authenticated on stage "var_stage" with tenant "var_tenant" 之前找不到所需的参数 stage 和 tenant 作为 fixtures。
如果你想在后续步骤中将 pytest-bdd 步骤用作 fixture,你需要按照文档中描述的方式调整 @scenario 装饰器;移除 @fixture 部分,并将 target_fixture="\<fixture-name\>" 添加到 @scenario 中。
因此,经过调整后,你的 http_session 函数将如下所示:
@given(parsers.parse('The user is authenticated on stage "{stage}" with tenant "{tenant}"'), target_fixture="http_session")
def http_session(config, stage, tenant, scope='session'):
    # ...
英文:
The error occurs because pytest tries to run http_session as a fixtures but cannot find the required parameters stage and tenant as fixtures before pytest-bdd even executes the step Given The user is authenticated on stage "var_stage" with tenant "var_tenant".
If you want to use a pytest-bdd step as fixture in subsequent steps, you have to adjust the @scenario decorator as described in the docs; remove the @fixture part and add target_fixture="<fixture-name>" to @scenario.
So your http_session function would look like the following after the adjustment:
@given(parsers.parse('The user is authenticated on stage "{stage}" with tenant "{tenant}"'), target_fixture="http_session")
def http_session(config, stage, tenant, scope = 'session'):
    # ...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论