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.
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.
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'):
# ...