Testing class methods with pytest - python

In the documentation of pytest various examples for test cases are listed. Most of them show the test of functions. But I’m missing an example of how to test classes and class methods. Let’s say we have the following class in the module cool.py we like to test:
class SuperCool(object):
def action(self, x):
return x * x
How does the according test class in tests/test_cool.py have to look?
class TestSuperCool():
def test_action(self, x):
pass
How can test_action() be used to test action()?

All you need to do to test a class method is instantiate that class, and call the method on that instance:
def test_action(self):
sc = SuperCool()
assert sc.action(1) == 1

Well, one way is to just create your object within the test method and interact with it from there:
def test_action(self, x):
o = SuperCool()
assert o.action(2) == 4
You can apparently use something like the classic setup and teardown style unittest using the methods here: http://doc.pytest.org/en/latest/xunit_setup.html
I'm not 100% sure on how they are used because the documentation for pytest is terrible.
Edit: yeah so apparently if you do something like
class TestSuperCool():
def setup(self):
self.sc = SuperCool()
...
# test using self.sc down here

I would use any fixtures only to create test environment (like database connection) or data parametrization.
If your data is relatively trivial, you can define it inside the testcase:
def test_action_without_fixtures():
sc = SuperCool()
sc.element = 'snow'
sc.melt()
assert sc.element == 'water'
Example with parametrization:
#pytest.mark.parametrize("element, expected", [('snow', 'water'), ('tin', 'solder')])
def test_action_with_parametrization(element, expected):
sc = SuperCool()
sc.element = element
sc.melt()
assert sc.element == expected

Related

Python unit test #mock.patch set mock property

Is there a way to have class patches with mocked properties set in once place? Say I have the following class I want to test.
class ExampleClass
def __init__(self, specific_args):
# Do something with specific args
self.specific_args = specific_args
self.sub_component = SubComponent(...)
def example_function_double_value(self) -> float:
return SomeFunction(self.specific_args, self.sub_component.value)
and it has the following unit test where I patch out SubComponent and set SubComponent.value to return 3.
import unittest
from unittest import mock
def set_patch(patch: mock.Mock):
patch_return_value = mock.Mock()
patch_return_value.value = 3
patch.return_value = patch_return_value
#mock.patch('SubComponent_Path.SubComponent')
class ExampleUnitTest(unittest.TestCase)
def test_case1(self, patch):
set_patch(patch)
specific_args1 = # Generate specific arguments for this test case
test_class = ExampleClass(specfic_args1)
# Some assertion where I expect `subcomponent.value to be 3`
def test_case2(self, patch):
set_patch(patch)
specific_args2 = # Generate specific arguments for this test case
test_class = ExampleClass(specfic_args2)
# Some other assertion where I expect `subcomponent.value to be 3`
Is there a more elegant way to do this without needing to call that helper function at the start of each test?
Setting return_value attribute of the mock is an efficient way to do it so we don't need to call the helper function.
import unittest
from unittest import mock
#mock.patch('SubComponent_Path.SubComponent')
class ExampleUnitTest(unittest.TestCase)
def setUp(self):
self.example_class = ExampleClass()
self.mocked_sub_component = self.example_class.sub_component
self.mocked_sub_component.value.return_value = 3
def test_case1(self, patch):
self.assertEqual(self.example_class.example_function_double_value, 6)
def test_case2(self, patch):
# Some other test case where I expect `subcomponent.value to be 3`

How to mock a method from the same class that the method being tested?

I have the following project structure
repo
|
|--utils
|----hudi.py
|----__init__.py
|--tests
|----test_hudi.py
|----__init__.py
I want to test the class in hudi.py, which is:
class Partitions:
def __init__(self, bucket, table)
self.bucket = bucket
self.table = table
def get_partitions(self):
return [
f"s3://{self.bucket}/{self.table}{i}"
for i in list(range(10))
if self.path_exists(i)
]
def path_exists(self, i):
s3_paths = code using external libraries
if s3_paths:
return True
else:
return False
I have written the code for testing in the file test_hudi.py as follow:
from utils import hudi
class TestHudi(unittest.TestCase):
#classmethod
def setUpClass(cls) -> None:
cls.hudi_partitions = hudi.Partitions(
table="signal",
bucket="bkt_name",
)
def test_get_partitions(self):
p = self.hudi_partitions.get_partitions()
assert p = ["s3://bkt_name/signal1"]
For being able to execute the test as above, I need to mock the function path_exist, and make its returned value true, so I have tried to mock it with the following patches:
#mock.patch("utils.hudi.path_exists", return_value=True)
#mock.patch("utils.hudi.partitions.path_exists", return_value=True)
#mock.patch("hudi_partitions.path_exists", return_value=True)
#mock.patch("hudi.partitions.path_exists", return_value=True)
#mock.patch("hudi.path_exists", return_value=True)
And none of them work.
Is there any way I can mock the function path_exists? Thanks!
To my understanding you should be mocking the use of external library. Also, this way you can test what happens when path does or doesn't exist.
If you still would like to accomplish this consider the following. You can actually replace a class function reference in python quite easily:
>>> class Test:
... def dummy(self):
... print("Original Test")
...
>>> test = Test()
>>> test.dummy()
Original Test
>>> def new_dummy():
... print("New Test")
...
>>> test.dummy = new_dummy
>>> test.dummy()
New Test

How to test an Object/Class with all the posible constructor arguments permutations?

I have the next class that I want to test with pytest:
class Analysis(object, metaclass=abc.ABCMeta):
def __init__(self, filters, and_operator, filters_comb_type):
self.filters = filters
self.andOperator = and_operator
self.filtersComb_type = filters_comb_type
def _generate_defaultfilters_combination(self):
...
def _generate_allfilters_combinations(self):
...
def _check_filters(self, pattern_indexes, direction=None):
...
So, I want to create a lot of possible Analysis() with a different set of possible values for filters, and_operator and filters_comb_type.
So I have written the next code using pytest:
import pytest
from src.statistical_analysis.analysis import Analysis, FiltersCombinationType
from tests.analysis_filters.mocks_analysis_filter import *
#pytest.fixture(params=[[],
[MockAllwaysTrueAnalysisFilter()],
[MockAllwaysFalseAnalysisFilter()],
[MockEvenTrueOddFalseAnalysisFilter()],
[MockAllwaysTrueAnalysisFilter(), MockAllwaysFalseAnalysisFilter(), MockAllwaysFalseAnalysisFilter()]])
def filters(request):
return request.param
#pytest.fixture(params=[True, False])
def and_operator(request):
return request.param
#pytest.fixture(params=[FiltersCombinationType.ONLY_DEFAULTS,
FiltersCombinationType.ONE_COMBINATION,
FiltersCombinationType.ALL_COMBINATIONS])
def filters_comb_type(request):
return request.param
#pytest.fixture
def analysis():
a = Analysis(filters, and_operator, filters_comb_type)
return a
So, if I have understand well the analysis() fucntion will create all the possible combinations of Analysis() object.
But my problem comes when I want to write the code to test the 3 methods of Analysis class, beause if I write this:
def test_check_filters(analysis):
...
The test it's going to be called with each of the whole Analysis objects created in the fixture, but now how can I compare each Analsysis with his expected return value of the check_filters() method??
I want to know which one is the correct aproach when we want to test a method of a class creating a lot of instances of a class with differente constructor parameters.

DI with unit tests called from elsewhere in application?

I am attempting to do something similar to the following:
import unittest
class foo:
one = 1
two = 1
class bar:
one = 2
two = 2
class my_test(unittest.TestCase):
def __init__(self, di_source):
self.di = di_source
print 'initing my_test'
def setUp(self):
print 'setting up!'
def tearDown(self):
print 'tearing down :('
def test_case_one(self):
self.assertEqual(self.di.one,1)
def test_case_two(self):
self.assertEqual(self.di.two, 2)
di_one = foo()
di_two = bar()
# called from elsewhere in my application
test_one = my_test(di_one).run()
test_one = my_test(di_two).run()
My goal is to:
Be able to call run() on a test suite
Provide a DI container at runtime to that test suite
Take advantage of the setUp and tearDown functionality provided by the unit test framework
However, it seems when I attempt to do this that the unittest framework doesn't like my constructor:
AttributeError: 'my_test' object has no attribute '_testMethodName'
Is there a better way to structure this example to avoid this problem?
How about using something like this:
This allows you to create shared resources for a single suite, pass the resource to all unittests, and then test the object with multiple methods.
'''Example using a shared resource in a unittest'''
import unittest
def callable_function():
'''Generic callable_function, this should actually be connected to an object constructor or something else''
return {'a': 3}
class MyTest(unittest.TestCase):
'''Custom unittest test case'''
def __init__(self, resource, method_name):
super(MyTest, self).__init__(method_name)
self._resource = resource
def test_getitem(self):
'''Test getting item'''
self.assertEquals(self._resource['a'], 3)
def test_setitem(self):
'''Test getting item'''
self._resource['b'] = 2
self.assertEquals(self._resource['b'], 2)
def test_mutable(self):
'''Test changes persist across tests'''
self.assertEquals(self._resource['b'], 2)
def run_suite():
'''Run complete unittest suite'''
suite = unittest.TestSuite()
item = callable_function()
suite.addTests([
MyTest(item, 'test_getitem'),
MyTest(item, 'test_setitem'),
MyTest(item, 'test_mutable'),
])
runner = unittest.TextTestRunner()
runner.run(suite)
if __name__ == '__main__':
run_suite()
EDIT: If you need to discover methods on the fly, you can do the following:
import inspect
def get_tests(cls):
return [k for k, v in cls.__dict__.items() if k.startswith('test') and inspect.ismethod(v)]
for name in get_tests(MyTest):
suite.addTest(MyTest(resource, name))
The idea is simple: override the __init__ method so it takes a resource and method name, bind the resource to the class, and initialize the TestCase as normal.
When you run the test, just use the bound resource.

How do you mock patch a python class and get a new Mock object for each instantiation?

OK,
I know this is mentioned in the manual, and probably has to do with side_effect and/or return_value, but a simple, direct example will help me immensely.
I have:
class ClassToPatch():
def __init__(self, *args):
_do_some_init_stuff()
def some_func():
_do_stuff()
class UUT():
def __init__(self, *args)
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
Now, I want to unit test the UUT class, and mock the ClassToPatch. Knowing the UUT class will instantiate exactly two ClassToPatch objects, I want the Mock framework to return a new Mock object for each instantiation, so I can later assert calls on each separately.
How do I achieve this using the #patch decorator in a test case? Namely, how to fix the following code sample?
class TestCase1(unittest.TestCase):
#patch('classToPatch.ClassToPatch',autospec=True)
def test_1(self,mock1,mock2):
_assert_stuff()
Here's a quick'n'dirty example to get you going:
import mock
import unittest
class ClassToPatch():
def __init__(self, *args):
pass
def some_func(self):
return id(self)
class UUT():
def __init__(self, *args):
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
self.test_property = (resource_1.some_func(), resource_2.some_func())
class TestCase1(unittest.TestCase):
#mock.patch('__main__.ClassToPatch', autospec = True)
def test_1(self, mock1):
ctpMocks = [mock.Mock(), mock.Mock()]
ctpMocks[0].some_func.return_value = "funky"
ctpMocks[1].some_func.return_value = "monkey"
mock1.side_effect = ctpMocks
u = UUT()
self.assertEqual(u.test_property, ("funky", "monkey"))
if __name__ == '__main__':
unittest.main()
I've added test_property to UUT so that the unit test does something useful. Now, without the mock test_property should be a tuple containing the ids of the two ClassToPatch instances. But with the mock it should be the tuple: ("funky", "monkey").
I've used the side_effect property of the mock object so that a different instance of ClassToPatch is returned on each call in the UUT initialiser.
Hope this helps.
Edit: Oh, by the way, when I run the unit test I get:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Here is another version which is more generic to handle any number of instances created:
class TestUUT:
def test_init(self, mocker):
class MockedClassToPatchMeta(type):
static_instance = mocker.MagicMock(spec=ClassToPatch)
def __getattr__(cls, key):
return MockedClassToPatchMeta.static_instance.__getattr__(key)
class MockedClassToPatch(metaclass=MockedClassToPatchMeta):
original_cls = ClassToPatch
instances = []
def __new__(cls, *args, **kwargs):
MockedClassToPatch.instances.append(
mocker.MagicMock(spec=MockedClassToPatch.original_cls))
MockedClassToPatch.instances[-1].__class__ = MockedClassToPatch
return MockedClassToPatch.instances[-1]
mocker.patch(__name__ + '.ClassToPatch', new=MockedClassToPatch)
UUT()
# since your original code created two instances
assert 2 == len(MockedClassToPatch.instances)
If you need more thorough validation for each instance you can access MockedClassToPatch.instances[0] or MockedClassToPatch.instances[1].
I've also created a helper library to generate the meta class boilerplate for me. To generate the needed code for your example I wrote:
print(PytestMocker(mocked=ClassToPatch, name=__name__).mock_classes().mock_classes_static().generate())

Categories