How to use multiple pytest fixtures with same parameterized input to test methods in a class - pytest

I want to achieve something like this:
my conftest.py will contain:
fixture_1 - will do some manipulation with input such as a, b, c using request.param
fixture_2 - will do some manipulation with input such as a, b, c using request.param
fixture_3 - will do some manipulation with input such as a, b, c using request.param
#pytest.mark.parametrize('param_name', ['a', 'b', 'c'], indirect=True)
class TestDummy(object):
def test_x(self, fixture_1):
fixture_1_output = fixture_1
assert False
def test_y(self, fixture_2):
fixture_2_output = fixture_2
assert False
def test_z(self, fixture_3):
fixture_3_output = fixture_3
assert False
Any suggestion would be highly appreciated.

You cannot directly patameterize the fixture name this way. Either you use parametrize for each test separately:
def params():
return ['a', 'b', 'c']
class TestDummy:
#pytest.parametrize(fixture_1, params, indirect=True)
def test_x(self, fixture_1):
fixture_1_output = fixture_1
assert False
#pytest.parametrize(fixture_2, params, indirect=True)
def test_y(self, fixture_2):
fixture_2_output = fixture_2
assert False
#pytest.parametrize(fixture_3, params, indirect=True)
def test_z(self, fixture_3):
fixture_3_output = fixture_3
assert False
(I moved the parameters into an extra function for convenience)
Or you have to do the parametrization dynamically based on the fixture name using pytest_generate_tests:
def pytest_generate_tests(metafunc):
fixtures = [f for f in metafunc.fixturenames if f.startswith("fixture_")]
if len(fixtures) == 1:
metafunc.parametrize(fixtures[0], ['a', 'b', 'c'], indirect=True)
class TestDummy:
def test_x(self, fixture_1):
fixture_1_output = fixture_1
assert False
def test_y(self, fixture_2):
fixture_2_output = fixture_2
assert False
def test_z(self, fixture_3):
fixture_3_output = fixture_3
assert False
def test_something_else(self):
pass
You need to determine which tests to parametrize - in this example all tests are parametrized which have exactly one fixture with a name starting with "fixture_", you may have to adapt ths to your needs.
In this example, the first 3 tests will be parametrized, but not the last one (test_something_else).

Related

How to isolate pytest tests

I'm running a test with class fixture, and how to make test_b and test_c to be isolated, such that both started with a_list = ['a'] only.
I know changing the fixture scope to function would achieve what I asked. But the fixture have to be class scoped. Is there some way to tell test_b and test_c to take a snapshot of a_list before running?
#pytest.fixture(scope="class")
def a_list(self):
return ['a']
def test_b(self,a_list):
a_list.append('b')
assert a_list == ['a','b']
def test_c(self,a_list):
assert a_list == ['a','b']
You can work on a copy of a_list in each test you want to modify it
#pytest.fixture(scope="class")
def a_list(self):
return ['a']
def test_b(self, a_list):
a_list = a_list[:]
a_list.append('b')
assert a_list == ['a', 'b']
def test_c(self, a_list):
assert a_list == ['a', 'b']
Output
PASSED [ 50%]
FAILED [100%]
example_test.py:31 (Test.test_c)
['a'] != ['a', 'b']
Expected :['a', 'b']
Actual :['a']
<Click to see difference>
self = <example_test.Test object at 0x0000011696077A90>, a_list = ['a']
def test_c(self, a_list):
> assert a_list == ['a', 'b']
E AssertionError: assert ['a'] == ['a', 'b']
example_test.py:35: AssertionError

Parametrize a Pytest using a dictionary with key/value individual pairs mapping

Have multiple tests in one test class. I would like to use a dictionary to parametrize the class.
Dictionary structure: {key1: [val_1, val2], key2: [val_3, val4]}
Test:
#pytest.mark.parametrize('key, value', [(k, v) for k, l in some_dict.items() for v in l], scope='class')
# ^^^ the best solution that I've found but still not as expected, and without IDS ^^^
class TestSomething:
def test_foo(self):
assert True
def test_boo(self):
assert True
Expected order (ids including, both key and values are objects and can provide '.name' property):
<Class TestSomething>
<Function test_foo[key1_name-val1_name]>
<Function test_boo[key1_name-val1_name]>
<Function test_foo[key1_name-val2_name]>
<Function test_boo[key1_name-val2_name]>
<Function test_foo[key2_name-val3_name]>
<Function test_boo[key2_name-val3_name]>
<Function test_foo[key2_name-val4_name]>
<Function test_boo[key2_name-val4_name]>
How can I add ids for this parametrize?
Here is a solution using a call to an external function in charge of formatting names from parameters value.
def idfn(val):
# receive here each val
# so you can return a custom property
return val.name
#pytest.mark.parametrize(
"key, value",
[(k, v) for k, l in some_dict.items() for v in l],
scope="class",
ids=idfn,
)
class TestSomething:
def test_foo(self, key, value):
assert True
But the simple solution with a lambda suggested by MrBean also works. In your simple case I would pick this one and use the external function only when more complex formatting is required.
#pytest.mark.parametrize(
"key, value",
[(k, v) for k, l in some_dict.items() for v in l],
scope="class",
ids=lambda val: val.name,
)
class TestSomething:
def test_foo(self, key, value):
assert True
The available options are presented in the doc

Scala Add elements to set only by checking specific fields of a case class

I have a case class
case class ApiCall(a: String, b: String, c: String, d: String, e: String, f: String)
I also have a mutable Set: private var apiCalls: mutable.Set[ApiCall] = mutable.Set[ApiCall]()
The problem is, I may get the following ApiCall elements:
ApiCall(a1, b1, c1, d1, e1, f1)
ApiCall(a1, b1, c1, d2, e2, f2)
I have to add elements to the set only if a unique combination of (a, b, c) of the case class ApiCall doesn't already exist. I cannot modify the case class itself because it is being used in multiple places.
Is it possible to add case class elements to the set only by looking at certain fields, and not all?
You might want to use Map instead of Set in your case:
val apiCalls = mutable.Map[(String, String, String), ApiCall]()
Also values are replaced for matching keys inside Map, you might need a separate method to update the API calls map:
def updateApiCalls(call: ApiCall): Unit = {
apiCalls.getOrElseUpdate((call.a, call.b, call.c), call)
()
}
I kind of solved it using a small workaround:
private var _apiCalls: mutable.Set[ApiCall] = mutable.Set[ApiCall]() is my Set of ApiCalls
I wrote a function which will add to set only if there's no 3 part key (a, b, c) already existing in the Set:
def addApiCall(e: X): Unit = {
val m = _x.find(m => m.a == e.a && m.b == e.b && m.c == e.c)
if (m.isEmpty)
_x += e
}
I'm not going to have too many elements in the Set either, so I found this approach easier to handle for me..

Use generator with ruamel.yaml

I would like to have a bunch of generators in my config dict. So I tried this:
#yaml.register_class
class UniformDistribution:
yaml_tag = '!uniform'
#classmethod
def from_yaml(cls, a, node):
for x in node.value:
if x[0].value == 'min':
min_ = float(x[1].value)
if x[0].value == 'max':
max_ = float(x[1].value)
def f():
while True:
yield np.random.uniform(min_, max_)
g = f()
return g
However, the parser never returns because generators are used internally to resolve reference like &A and *A. Therefore, something like returning (g,) is a fairly simple workaround, but I would prefer a solution where I don't need the additional and very confusing index 0 term in next(config['position_generator'][0]).
Any Ideas?
This wrapper adapted from a different question did exactly what I was looking for.
class GeneratorWrapper(Generator):
def __init__(self, function, *args):
self.function = function
self.args = args
def send(self, ignored_arg):
return self.function(*self.args)
def throw(self, typ=None, val=None, tb=None):
raise StopIteration
#yaml.register_class
class UniformDistribution:
yaml_tag = '!uniform'
#classmethod
def from_yaml(cls, constructor, node):
for x in node.value:
value = float(x[1].value)
if x[0].value == 'min':
min_ = value
if x[0].value == 'max':
max_ = value
return GeneratorWrapper(np.random.uniform, min_, max_)

How to set a list of pytest markers to a test?

I am using 2 markers '#pytest.mark.regression' and '#pytest.mark.smoke'. I need all my tests to have these 2 markers, so I wanted to create a list like
allmarkersList = [pytest.mark.regression, pytest.mark.smoke] and I want to call this marker #allmarkersList to all my tests, is this possible ?
import pytest
allmarkers = [pytest.mark.regression, pytest.mark.smoke]
#pytest.mark.regression
def test_reg1():
assert True
#pytest.mark.regression
def test_reg2():
assert True
#pytest.mark.smoke
def test_smoke1():
assert True
#pytest.mark.smoke
def test_smoke2():
assert True
#allmarkers
def test_reg_and_smoke():
print(all_marks)
assert True
when I run this I get the below error -
#allmarkers
E TypeError: 'list' object is not callable