pytest. Change default values of arguments of fixture - pytest

Hi I have a fixture with default arguments, like
pytest.fixture(scope='function', params =[{'arg1':value1}, {'arg2':Value2}])
def some_fixture(request):
if request.param['arg1'] == value1:
'do some actions'
if request.param['arg2'] ==value2:
'do some actions'
but now i see an error:
def some_fixture(request):
if request.param['arg1']
KeyError: arg1

Related

Pytest: Passing on multiple parameters to a fixture

I would like to use a flexible fixture for a data call that can be reused across the test suite. For this purpose I like to ideally pass on more than on parameter. However the code fragment below generally works only for one parameter:
#pytest.fixture
def getData(request):
"""Variable data query"""
data_detail = request.param
In the pytest documentation there is no hint that more than one parameter may work, e.g. such as:
#pytest.fixture
def getData(request):
"""Variable data query"""
data_detail = request.param[0]
time_detail = request.param[1]
Does anyone have a hint how to build a pytest fixture to which several parameters / arguments may be passed on?
You can use a tuple or a dict as the fixture parameter:
#pytest.fixture
def tuple_params(request):
yield sum(request.param)
#pytest.mark.parametrize("tuple_params", [(1, 2, 3)], indirect=True)
def test_tuple_params(tuple_params):
print(tuple_params) # 6
#pytest.fixture
def dict_params(request):
yield f"{request.param['a']}_{request.param['b']}"
#pytest.mark.parametrize("dict_params", [{"a": "foo", "b": "bar"}],
indirect=True)
def test_dict_params(dict_params):
print(dict_params) # foo_bar
Generally, the parameter can be any object, so you can always put your fixture parameters in a suitable object.
With a tuple or a list parameter, you can also access the values per index as in your example.

Pytest dynamic fixture scope - how to set and will the fixture scope apply to all tests?

I am trying to use pytest fixtures dynamic scoping.
The docs state that the scope will be determined during fixture definition; does it mean that once the scope is dynamically set in pytest run , this scope will apply to all tests using the fixture?
Is it possible to affect the scope during the test run (i.e using markers)?
If not, how can I change the config (without using a command line arg) to change the scope?
I tried to add a marker and use its value as a scope in pytest_generate_tests, however fixture definition runs prior to pytest_generate_tests.
def pytest_generate_tests(metafunc):
fixture_scope = metafunc.definition.get_closest_marker('scope').args
if fixture_scope is not None:
metafunc.config.addinivalue_line('markers', f'scope:{fixture_scope[0]}')
def determine_scope(fixture_name, config):
scope = [i for i in config.getini('markers') if 'scope' in i][0]
if scope:
return scope.split(':')[-1]
return "function"
#pytest.fixture(scope=determine_scope)
def some_fixture():
I would suggest using env var as scope. And change the env var if you want to change the scope. Scope change will only take effect once fixture's current scope ends.
#pytest.fixture(scope=os.environ.get("fixture_scope","function))
def fixture_name():
pass
def test_change_fixture():
os.environ["fixture_scope"] = "class"
I have not tested this code. But I have found use of env var very useful in passing variables between different stages of pytest run. You can also use dynamic config file or something similar.
An ugly way to achieve that is to have 4 copies of same fixture and get the relevant fixture as needed.
def fixture_code():
pass
#pytest.fixture(scope="module)
def module_fixture():
fixture_code()
#pytest.fixture(scope="session")
def session_fixture():
fixture_code()
# similar fixtures for other scopes
#pytest.fixture(scope="session")
def fixture_provider(request, module_fixture, session_fixture, .....):
if request.param['scope'] == "module":
return module_fixture
elif request.param['scope'] == "session":
return session_fixture
.
.
.
#pytest.mark.parametrize("scope",[os.environ.get("fixture_scope")], indirect=True)
def test_abc(fixture_provider):
#use fixture provider.
You can set env var with code to get relevant fixture.

Passing arguments to instantiate object in Pytest

I have a class which I would like to instantiate using different sets of input parameters, comparing a property on the resultant object to a passed in value.
I am using the indirect flag on #pytest.fixture for the arguments which are sent to the class constructor. I am trying to unpack kwargs in the constructor. Unsuccesfully. This is the error:
TypeError: type object argument after ** must be a mapping, not SubRequest
Code:
import pytest
class MyClass:
def __init__(self, a):
self.a = a
#pytest.fixture
def my_object(request):
yield MyClass(**request)
# first element = arguments to MyClass, second element = value to compare test to
TEST_CASES = [({"a":1}, 1)]
#pytest.mark.parametrize("test, expected", TEST_CASES, indirect=["test"])
def test_1(my_object, test, expected):
assert my_object.a == expected
My goal is to have the object arguments and their test value TEST_CASES in one structure for easy inspection
I've suggest you a working example. Problem was in test code design. The parameter indirect should be True. Indirect parametrization with multiple fixtures should be done as described in docs. And fixture got all params in his request.param attribute.
import pytest
class MyClass:
def __init__(self, a):
self.a = a
#pytest.yield_fixture
def test_case(request):
params, expected = request.param
yield MyClass(**params), expected
# first element = arguments to MyClass, second element = value to compare test to
TEST_CASES = [({"a": 1}, 1)]
#pytest.mark.parametrize("test_case", TEST_CASES, indirect=True)
def test_1(test_case):
my_object, expected = test_case
assert my_object.a == expected

Pytest yield fixture usage

I have a use case where I may use fixture multiple times inside a test in a "context manager" way. See example code below:
in conftest.py
class SomeYield(object):
def __enter__(self):
log.info("SomeYield.__enter__")
def __exit__(self, exc_type, exc_val, exc_tb):
log.info("SomeYield.__exit__")
def generate_name():
name = "{current_time}-{uuid}".format(
current_time=datetime.now().strftime("%Y-%m-%d-%H-%M-%S"),
uuid=str(uuid.uuid4())[:4]
)
return name
#pytest.yield_fixture
def some_yield():
name = generate_name()
log.info("Start: {}".format(name))
yield SomeYield()
log.info("End: {}".format(name))
in test_some_yield.py
def test_some_yield(some_yield):
with some_yield:
pass
with some_yield:
pass
Console output:
INFO:conftest:Start: 2017-12-06-01-50-32-5213
INFO:conftest:SomeYield.__enter__
INFO:conftest:SomeYield.__exit__
INFO:conftest:SomeYield.__enter__
INFO:conftest:SomeYield.__exit__
INFO:conftest:End: 2017-12-06-01-50-32-5213
Questions:
If I have some setup code in SomeYield.enter and cleanup code in
SomeYield.exit, is this the right way to do it using fixture for
multiple calls in my test?
Why didn't I see three occurrences of
enter and exit? Is this expected?

How to pass variable from test function to fixture in pytest?

Below is the example code from contest.py
#pytest.fixture
def required_input(var1): # not sure if i can pass var1 to the fixture.
return var1+"_test"
below is the test case from test_code.py
def test_case1(required_input):
x = required_input("tt") # Not sure if it can be called like this.
assert 'tt_test' == x
How can I pass variable from my test function to a fixture?
A fixture can be a function:
#pytest.fixture
def required_input(): # not sure if i can pass var1 to the fixture.
return lambda var1: return var1+"_test"