Calling class level variables using "#pytest.mark.parametrize" fixture - python-3.x

I'm trying to iterate a pytest test over a list of values which i generate inside another method in the test class.
The problem is that i get:
"#pytest.mark.parametrize("number",TestScratch.list_testing)
NameError: name 'TestScratch' is not defined" error when i try to run.I know for a fact that when i pass the list as a hard coded list i.e [0,3,54,90] it works.
Below is my code :
class TestScratch(object):
#classmethod
def setup_class(cls):
cls.list_testing = []
#classmethod
def setup_method(cls):
pass
def test_populate_list(self):
for i in range(100):
self.list_testing.append(i)
#pytest.mark.parametrize("number",TestScratch.list_testing)
def test_pytest_param(self, number):
assert type(number) == int
#classmethod
def teardown_class(cls):
'''
pass
'''
I also tried self.class.list_testing
but i get the same error
Environment details:
Python: 3.6.8
Pytest: 5.2.1

You can't use the class in the class definition. As the decorator is read at import time, e. g. while loading the class definition, and not at runtime, the class is not known at this point. You have to define the list outside the class instead:
import pytest
def populate_list():
test_list = []
for i in range(100):
test_list.append(i)
return test_list
list_testing = populate_list()
class TestScratch:
def test_populate_list(self):
# this will fail if list_testing could not be populated
assert len(list_testing) > 50
#pytest.mark.parametrize("number", list_testing)
def test_pytest_param(self, number):
# this will be skipped if list_testing could not be populated
assert type(number) == int
Any parameter you use in a decorator is read only once at load time, so trying to intialize it at runtime won't work. For example here you can find an explanation how parametrization works and why it is not possible to add parameters at runtime.

Related

how to call fucntion which contain nested dictionary from another python script

I am trying to get the nested dictionary data from another python script.
As an example below
main.py
class data():
def __init__(self):
self.result()
def result(self):
self.output_data = {}
def get_list(self):
return self.output_data
--------------------------------------------------------
script.py
from main import data
class communix():
def __init__(self):
def new_data(self):
want to fetch the nested dictionary from get_list function
I made a few modifications in your code, there are some hints I'd like you to note:
In python, use CamelCase naming convention for your class names.
You can mix your two result() and get_list() methods into one method.
Why don't you use inheritence in your second class?
So, regarding points mentionned above, this code worked for me:
main.py
class Data():
def __init__(self):
self.result()
def result(self):
self.output_data = {'data': 'sample data'}
return self.output_data
script.py
from main import Data
class Communix(Data):
def new_data(self):
data_extracted = data_object.result()
return data_extracted
data_object = Communix()
print(data_object.new_data())
output:
>>> {'data': 'sample data'}
I hope it helped you.

Specify class variable in Python to be a numpy array of not yet known size

I have a class like
class MyClass:
def __init__(self):
self.will_be_a_numpy_array = None
def compute():
tmp = receive_data()
self.will_be_a_numpy_array = np.zeros(len(tmp))
# process each item in tmp, save result in corresponding element of self.will_be_a_numpy_array
Here __init__ method is vague regarding the type of self.will_be_a_numpy_array variable. It is unclear to fellow developer or compiler what type of variable should be expected. I cannot initialize variable with self.will_be_a_numpy_array = np.zeros(len(tmp)) because I haven't received data yet. What is the right way to articulate variable type in this case?
You can use the strategy that scikit-learn uses for their estimators, namely, you create the attribute when you receive the data and you use a trailing underscore to warn that this is an attribute that is not created at initialisation:
class MyClass:
def __init__(self):
pass
def process(self, data):
self.data_ = np.array(data)
def is_processed(self):
return hasattr(self, 'data_')

python pytest - How can I get a test markers inside its fixture methods?

I have test in pytest and added some markers such as: '#pytest.mark.xfail', the test have several fixture methods.
How can I get all the markers of certain test using a fixture method?
The markers can be taken from the fixture request node attribute using iter_markers, as can be seen in this (nonsensical) example:
import pytest
#pytest.fixture
def get_marks(request):
marks = [m.name for m in request.node.iter_markers()]
if request.node.parent:
marks += [m.name for m in request.node.parent.iter_markers()]
yield marks
#pytest.mark.parametrize('number', [1, 2, 3])
#pytest.mark.foo
#pytest.mark.xfail
def test_marks(get_marks, number):
print(get_marks)
assert 'xfail' in get_marks
assert number == 42
This gives the output:
...
xfail [100%]['parametrize', 'xfail', 'foo']
get_marks = ['parametrize', 'xfail', 'foo'], number = 3
#pytest.mark.parametrize('number', [1, 2, 3])
#pytest.mark.foo
#pytest.mark.xfail
def test_marks(get_marks, number):
print(get_marks)
assert 'xfail' in get_marks
> assert number == 42
E assert 3 == 42
Update:
Use the correct public call to get the markers as proposed by #hoefling.
One method is already described by #MrBean, here's one another approach using pytest_runtest_setup in conftest.py. By combiantion of global variable and pytest_runtest_setup which is pytest's own method, you can get current test marker.
This approach would give you markers of both class level and function level.
conftest.py
import pytest
marker_name = [] #Global variable so you can use it everywhere
def pytest_runtest_setup(item):
global marker_name
marker_name.clear() #If you would not clear this, it would append every test's marker, test by test.
for mark in item.iter_markers():
marker_name.append(mark.name)
#pytest.fixture(scope='function', autouse=True) #Note here, autouse=true.
def my_fixture(request):
request.instance.test_marker=marker_name #assign it to request's session so you can use it in other fixture and in tests as well
#pytest.fixture
def some_other_fixture(request):
test_marker=request.instance.test_marker #access marker in fixture
test_file.py (Access that marker variable inside test using request.instance.test_marker.)
import pytest
#pytest.mark.class_marker
class TestClass:
#pytest.mark.my_test
#pytest.mark.xfail
def test_foo(self,request):
print(request.instance.test_marker) #output : ['xfail', 'my_test', 'class_marker']
#pytest.mark.my_test_two
#pytest.mark.xfail
def test_foo_two(self,request):
print(request.instance.test_marker) #output : ['xfail', 'my_test_two', 'class_marker']
Hope it would help !!

Pytest object created by object assert_called_once_with

I known how I can test if an injected object was called with a specific argument. But in my case the injected object will create an object that object will create another object and I want to test if that last object was called with the right argument.
in the example below the question would be if c.dirve was called with 100 as argument:
class car:
def drive(self, distance):
print("so fast")
class car_shop:
def buy_car(self):
return car()
class shop_shop:
def buy_shop(self):
return car_shop()
class processor:
def __init__(self, sshop):
self.sshop = sshop
def run(self):
cshop = self.sshop.buy_shop()
c = cshop.buy_car()
c.drive(100)
def main():
sshop = shop_shop()
proc = processor(sshop)
proc.run()
if __name__ == "__main__":
main()
is there a way to test that?
Since this was requested here my approach for testing these objects:
import pytest
from unittest.mock import Mock
from object_returns_object_test_for_arguments import processor, shop_shop
#pytest.fixture
def mock_shop_shop():
return Mock(spec=shop_shop)
def test_processor_car_called_with_100(mock_shop_shop):
proc = processor(mock_shop_shop)
proc.run()
assert mock_shop_shop.car_shop.car.drive.assert_called_once_with(100)
assert mock_shop_shop.car_shop.car.drive.call_count == 1
If using just the code shown in the question, you only have to mock car.drive. This could be done for example this way:
from unittest import mock
from object_returns_object_test_for_arguments import processor, shop_shop
#mock.patch('object_returns_object_test_for_arguments.car.drive')
def test_processor_car_called_with_100(drive_mock):
proc = processor(shop_shop())
proc.run()
drive_mock.assert_called_once_with(100)
As I don't know your real code, you may have to mock more stuff.
As an aside: class names in Python are written upper-case, camelcase-style by default.

Python subclass that takes superclass as argument on instantiation?

I am trying to create a wrapper class in Python with the following behaviour:
It should take as an argument an existing class from which it should inherit all methods and attributes
The wrapper class methods should be able to use Python super() to access methods of the superclass (the one passed as an argument)
Because of my second requirement I think the solution here will not suffice (and in any case I am having separate issues deepcopying some of the methods of the superclass' I am trying to inherit from).
I tried this but it's not correct...
class A:
def shout(self):
print("I AM A!")
class B:
def shout(self):
print("My name is B!")
class wrapper:
def __init__(self, super_class):
## Some inheritance thing here ##
# I initially tried this but no success...
super(super_class).__init__() # or similar?
def shout(self):
print('This is a wrapper')
super().shout()
And this is the behaviour I require...
my_wrapper = wrapper(A)
my_wrapper.shout()
# Expected output:
# > This is a wrapper
# > I AM A
my_wrapper = wrapper(B)
my_wrapper.shout()
# Expected output:
# > This is a wrapper
# > My name is B!
Is inheritance the correct approach here, if so am I sniffing in the right direction? Any help is appreciated, thanks :)
Edit for context:
I intend to build multiple wrappers so that all of my ML models have the same API. Generally, models from the same package (sklearn for example) have the same API and should be able to be wrapped by the same wrapper. In doing this I wish to modify/add functionality to the existing methods in these models whilst keeping the same method name.
If wrapper has to be a class then a composition solution would fit much better here.
Keep in mind that I turned the shout methods to staticmethod because in your example you pass the class to wrapper.shout, not an instance.
class A:
#staticmethod
def shout():
print("I AM A!")
class B:
#staticmethod
def shout():
print("My name is B!")
class wrapper:
def __init__(self, super_class):
self._super_class = super_class
def __getattr__(self, item):
try:
return self.__dict__[item].__func__
except KeyError:
return self._super_class.__dict__[item].__func__
def a_wrapper_method(self):
print('a wrapper attribute can still be used')
my_wrapper = wrapper(A)
my_wrapper.shout()
my_wrapper = wrapper(B)
my_wrapper.shout()
my_wrapper.a_wrapper_method()
Outputs
This is a wrapper
I AM A!
This is a wrapper
My name is B!
a wrapper attribute can still be used
So I went for a function in the end. My final solution:
class A:
def shout(self):
print("I AM A!")
class B:
def shout(self):
print("My name is B!")
def wrap_letter_class(to_wrap):
global letterWrapper
class letterWrapper(to_wrap):
def __init__(self):
super().__init__()
def shout(self):
print('This is a wrapper')
super().shout()
def __getstate__(self):
# Add the wrapper to global scope before pickling
global letterWrapper
letterWrapper = self.__class__
return self.__dict__
return letterWrapper()
Which produces the desired behaviour...
In [2]: wrapped = wrap_letter_class(A)
In [3]: wrapped.shout()
This is a wrapper
I AM A!
In [4]: wrapped = wrap_letter_class(B)
In [5]: wrapped.shout()
This is a wrapper
My name is B!
Something not mentioned in my initial question was that I intended to pickle my custom class, this is not possible if the class is not defined in the global scope, hence the __getstate__ and global additions.
Thanks!

Resources