Unable to run addfinalizer function within a fixture with scope as class - python

Here’s my test code:
import string
import pytest
import tempfile
import os
#pytest.fixture(scope='class')
def TempFile(request):
(tmp_cfg_fd, tmp_cfg_file_path) = tempfile.mkstemp()
os.close(tmp_cfg_fd)
def RemoveTempFile():
print("Removing %r" %(tmp_cfg_file_path))
os.remove(tmp_cfg_file_path)
request.addfinalizer(RemoveTempFile)
return tmp_cfg_file_path
#pytest.mark.usefixtures(TempFile)
class Test:
def test1(self):
print("I'm in test1")
def test2(self):
print("I'm in test2")
When I run py.test on it, I get this error:
test_4.py:17: in <module>
#pytest.mark.usefixtures(TempFile)
test_4.py:14: in TempFile
request.addfinalizer(RemoveTempFile)
E AttributeError: class Test has no attribute 'addfinalizer'
When the fixture has scope='class', then the Test class fails to run addfinalizer.
But if the fixture has scope='function', and I call the TempFile fixture individually in test1 and test2 functions, then addfinalizer runs properly.
How can I get addfinalizer to run with scope='class'?

The usefixtures mark takes an string, if you change the mark into:
#pytest.mark.usefixtures('TempFile')
it will work correctly.

Related

How to mock a function which gets executed during the import time?

Here the ABC() and obj.print_1() get called during the import time and it prints "making object" and "printed 1" respectively. How can we mock all the three functions, __init__(), print_1(), and print_2()?
xyz.py
from abc import ABC
obj = ABC()
obj.print_1()
def func():
return obj.print_2(2)
abc.py
class ABC():
def __init__(self):
print("making object")
def print_1(self):
print("printed 1")
return None
def print_2(self, val):
print("printed ", val)
return None
Indeed, as soon as you import xyz, it will import abc and create an instance then call a method on it.
Solution : import abc yourself BEFORE xyz EVER GETS IMPORTED, and mock the methods defined in the class. And because we can't import a method, patch.object is required.
Note : I added a self as parameter in your ABC.print_1 method, otherwise it would be incorrect. Otherwise make it #staticmethod
Here is the test file I used :
import unittest
import unittest.mock as mock
from so74709409_abc import ABC
# no import of `xyz` here !
class Tests(unittest.TestCase):
def test__xyz_obj_calls_print1(self):
# __init__ must return None
with mock.patch.object(ABC, "__init__", **{"return_value": None}) as mock_init, \
mock.patch.object(ABC, "print_1") as mock_print1, \
mock.patch.object(ABC, "print_2") as mock_print2:
from so74709409_xyz import func # import now !
func()
mock_init.assert_called_once()
mock_print1.assert_called_once_with()
mock_print2.assert_called_once_with(2)
if __name__ == "__main__":
unittest.main()
But this is not very robust, if the module was already imported (maybe indirectly) before the test run, the import inside the test won't have any effect, and so it will fail (mocks not getting called). It can be a pain in a real test suite (with many tests running in sequence) because the previous test will already have imported xyz.
That's why it's better to do these kind of things in a if __name__=="__main__", or in a function called deliberately.
(beware : I assume you choose abc as a dummy name, but it is actually a standard library module for Abstract Base Classes)

Run a specific unittest with loadTestsFromName

I want to run a single test and output the result to a txt file. I understood that I can use loadTestsFromName to specify the test. However, I'm getting an error.
test.py
import unittest
import sys
import os
def main(out=sys.stderr, verbosity=2):
loader = unittest.TestLoader()
suite = loader.loadTestsFromName(sys.modules[__name__]=='test1')
unittest.TextTestRunner(out, verbosity=verbosity).run(suite)
class TestClass(unittest.TestCase):
def test1(self):
self.assertEqual(True, True)
if __name__ == '__main__':
with open('test-results.txt', 'w') as f:
main(f)
I run the test by executing python test.py
I'm not sure how to get test1. I tried sys.modules[__name__]=='test1' but it triggered this error.
parts = name.split('.')
AttributeError: 'bool' object has no attribute 'split'
According to the python doc-unittest.TestLoader.loadTestsFromName, below code is work for me.
import unittest
import sys
import os
def main(out=sys.stderr, verbosity=2):
loader = unittest.TestLoader()
suite = loader.loadTestsFromName('__main__.TestClass.test1')
unittest.TextTestRunner(out, verbosity=verbosity).run(suite)
class TestClass(unittest.TestCase):
def test1(self):
self.assertEqual(True, True)
if __name__ == '__main__':
with open('test-results.txt', 'w') as f:
main(f)
Besides, you'd better separate the TestCase to the single module, then change the __main__ to the module name.

Mocking a method inside class in python test

I am new to Python and writing a unit test for the already existing code.
I have the following Python code structure:
root_project:
-- src
-- signUp
-- __init__.py
-- abc.py
-- xyz.py
abc.py contains the following code:
from . import xyz
class Abc:
def __init__(self):
pass
def start_process(self):
xyz.start_timer()
I want to mock xyz.start_timer() inside method start_process of class Abc so that start_timer() mock version can be used.
How can I achieve this?
You can simply patch the start_timer function using mock.patch().
abc.py
import xyz
class Abc:
def __init__(self):
pass
def start_process(self):
xyz.start_timer()
test_abc.py
from unittest import mock, TestCase
from abc import Abc
class Test_Abc(TestCase):
#mock.patch("abc.start_timer")
def test_start_process(self, mock_start_timer):
# your test code
Notice that I have patched abc.start_timer and not xyz.start_timer.
You just need to patchxyz.start_timer(), I just added return to start_process to show that the patching really does what it should:
import xyz
import unittest
from unittest import mock
class Abc:
def __init__(self):
pass
def start_process(self):
return xyz.start_timer()
class Test_Abc(unittest.TestCase):
#mock.patch('xyz.start_timer', return_value=True)
def test_start_process(self, start_process):
a = Abc()
self.assertTrue(a.start_process())
if __name__ == '__main__':
unittest.main()
Out:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

Check if Path.exists() was called once with mock.path in a unittest

I need to know if Path.exists() was called. I want to do that in a unittest but allways got the answer that it was not called.
I assume something is wrong with the import path. I know from the docs and some blog-posts that this can be tricky.
This is the "productive" code:
import pathlib
class Foo():
def __init__(self, file_path: pathlib.Path):
if not file_path.exists():
print('Not good.')
else:
print('Fine.')
And this is the unittest for it
import unittest
import unittest.mock
import pathlib
import mycode
class MyTest(unittest.TestCase):
#unittest.mock.patch('pathlib.Path')
def test_mycode(self, mock_path):
mypath = pathlib.Path('bar')
foo = mycode.Foo(mypath)
mock_path.exists.assert_called_once()
But the error is still
AssertionError: Expected 'exists' to have been called once. Called 0
times.
You can create mock for pathlib.Path using create_autospec helper function. And pass this mock object to the constructor of the Foo class.
Functions or methods being mocked will have their arguments checked to ensure that they are called with the correct signature.
E.g. (Python 3.9.6)
foo.py:
import pathlib
class Foo():
def __init__(self, file_path: pathlib.Path):
if not file_path.exists():
print('Not good.')
else:
print('Fine.')
foo_test.py:
import unittest
import pathlib
from unittest.mock import create_autospec
from foo import Foo
class MyTest(unittest.TestCase):
def test_Foo_file_path_exists(self):
mock_path = create_autospec(pathlib.Path)
mock_path.exists.return_value = True
Foo(mock_path)
mock_path.exists.assert_called_once()
def test_Foo_file_path_not_exists(self):
mock_path = create_autospec(pathlib.Path)
mock_path.exists.return_value = False
Foo(mock_path)
mock_path.exists.assert_called_once()
if __name__ == '__main__':
unittest.main()
Test result:
Fine.
.Not good.
.
----------------------------------------------------------------------
Ran 2 tests in 0.184s
OK
Name Stmts Miss Cover Missing
----------------------------------------------------------------------
src/stackoverflow/71945781/foo.py 6 0 100%
src/stackoverflow/71945781/foo_test.py 17 0 100%
----------------------------------------------------------------------
TOTAL 23 0 100%
The answer of #slideshowp2 is fine and working. And it pointed me to the right solution better fitting to my own MWE.
class MyTest(unittest.TestCase):
#unittest.mock.patch('pathlib.Path')
def test_mycode(self, mocked_path):
foo = mycode.Foo(mocked_path)
mocked_path.exists.assert_called_once()
The problem with my code was that I instantiated a real object of pathlib.Path. But the key of the solution is to use the mock object mocked_path because this is a surrogate for a real Path object.
In my questions intial code the Foo.__init__() never used the mocked object but the real pathlib.Path.

How to reference unittest TestClass attribute in #patch decorator

I want to make use of my tempfile module to write files in my test case.
But I am having trouble referencing it when usinng mock.patch
Supposed I have this code, notice the patch as it is referencing the self.test_dir
import unittest
from unittest.mock import patch
import tempfile
class TestSomething(unittest.TestCase):
def setUp(self):
self.test_dir = tempfile.TemporaryDirectory()
def tearDown(self):
# Close the file, the directory will be removed after the test
self.test_dir.cleanup()
#patch("sample_module.SampleClass.base_url", self.test_dir)
def test_override(self):
pass
Running this code will result to this error
NameError: name 'self' is not defined
As the test_dir is being initialized in the setup and tearDown method then how can I make use of this tempfile.TemporaryDirectory
Any thoughts?

Categories