Passing arguments to fixture in TestCase with pytest - python

How to pass custom arguments to fixture inside test method of unittest.TestCase derived class using pytest?

After searching for definitely too long time I managed to invent solution with usage of doc and threads about wrapping fixtures. I hope somebody will find it useful.
conftest.py
import pytest
#pytest.fixture()
def add(request):
def wrapped(a=10, b=5):
return a + b
request.cls.add = wrapped
add_test.py
import pytest
from unittest import TestCase
#pytest.mark.usefixtures('add')
class AddTestCase(TestCase):
def test_add(self):
# parameters can be passed inside test method
result = self.add(2, 2)
assert result == 4

Related

Check if pytest fixture is called once during testing

Does pytest provides functionality like unittest.mock to check if the mock was actually called once(or once with some parameter)?
Sample Source code:
my_package/my_module.py
from com.abc.validation import Validation
class MyModule:
def __init__(self):
pass
def will_call_other_package(self):
val = Validation()
val.do()
def run(self):
self.will_call_other_package()
Sample test code for the above source code:
test_my_module.py
import pytest
from pytest_mock import mocker
from my_package.my_module import MyModule
#pytest.fixture
def mock_will_call_other_package(mocker):
mocker.patch('my_package.my_module.will_call_other_package')
#pytest.mark.usefixtures("mock_will_call_other_package")
class TestMyModule:
def test_run(self):
MyModule().run()
#check `will_call_other_package` method is called.
#Looking for something similar to what unittest.mock provide
#mock_will_call_other_package.called_once
If you want to use a fixture that does the patching, you can move the patching into a fixture:
import pytest
from unittest import mock
from my_package.my_module import MyModule
#pytest.fixture
def mock_will_call_other_package():
with mock.patch('my_package.my_module.will_call_other_package') as mocked:
yield mocked
# the mocking will be reverted here, e.g. after the test
class TestMyModule:
def test_run(self, mock_will_call_other_package):
MyModule().run()
mock_will_call_other_package.assert_called_once()
Note that you have to use the fixture parameter in the test. Just using #pytest.mark.usefixtures will not give you access to the mock itself. You can still use it to be effective in all tests in the class, if you don't need to access the mock in all tests (or use autouse=True in the fixture).
Also note that you don't need pytest-mock here - but as mentioned by #hoefling, using it makes the fixture better readable, because you don't need the with clause :
#pytest.fixture
def mock_will_call_other_package(mocker):
yield mocker.patch('my_package.my_module.will_call_other_package')
As an aside: you don't need to import mocker. Fixtures are looked up by name, and available automatically if the respective plugin is installed.
You could try this:
import pytest
from my_package.my_module import MyModule
def test_run(mocker):
mocker.patch('my_package.my_module.will_call_other_package')
MyModule().run()
mock_will_call_other_package.assert_called_once()
First of all, you may not need the burden of an external library such as pytest_mock, because pytest already got you covered using the integration with unittest.
You also do not need to use the usefixtures because whenever you need a fixture, you just receive it in your test method.
An ideal scenario based on your own code would look similar to this:
import pytest
from unittest.mock import patch
from com.abc.validation import Validation
class MyModule:
def __init__(self):
pass
def will_call_other_package(self):
val = Validation()
val.do()
def run(self):
self.will_call_other_package()
#pytest.fixture
def call_other_module():
with patch("my_package.my_module.MyModule.will_call_other_package") as _patched:
yield _patched
class TestMyModule:
def test_run_will_call_other_package(self, call_other_module):
call_other_module.assert_not_called()
obj = MyModule()
call_other_module.assert_not_called()
obj.run()
call_other_module.assert_called_once()
And also if you want to make sure that you did infact patch the target MyModule.will_call_other_package, modify your test like this:
class TestMyModule:
def test_run_will_call_other_package(self, call_other_module):
call_other_module.assert_not_called()
obj = MyModule()
call_other_module.assert_not_called()
obj.run()
call_other_module.assert_called_once()
assert False, (MyModule.will_call_other_package, call_other_module)
And you'll see something similar to this:
AssertionError: (<MagicMock name='will_call_other_package' id='140695551841328'>, <MagicMock name='will_call_other_package' id='140695551841328'>)
As you can see the id of both objects are the same, confirming our experiment was successful.

Using assertRaises without self in Python 3.6

I would like to test one failure scenario in one of my python file as follows:
source.py
def myfunc():
a()
associated test.py
def testMyFuncException():
a = Mock()
a.side_effect = MyError
with self.assertRaises(MyError) as _ : <--- THIS LINE I CANNOT USE self.assertRaises
..
But here I cannot use self as it's not associated with any class. So I am not getting any clue regarding how to do this.
EDIT
I have done it as follows now as per suggestions:
def testMyFuncException(TestCase):
a = Mock()
a.side_effect = MyError
with TestCase.assertRaises(MyError) as _ :
...
Now I am getting error as follows:
E fixture 'TestCase' not found
Another possibility is to use pytest instead of unittest. In this case you don't need a separate test class:
import pytest
from unittest.mock import Mock
def test_myfunc_exception():
a = Mock()
a.side_effect = MyError
with pytest.raises(MyError, match="my exception message"):
...
You need a TestCase fixture in the unittest testing framework. The normal way to do this is to create a class inheriting from unittest.TestCase. You will then be able to use the self.assertRaises method, along with the other asserts in the TestCase class:
from unittest import TestCase
from unittest.mock import Mock
class TestMyFunc(TestCase):
def test_myfunc_exception(self):
a = Mock()
a.side_effect = MyError
with self.assertRaises(MyError) as _ :
...

Patching decorator in setUp or setUpClass in TestCases does not work

I am trying to patch some functions during either the setUp or setUpClass methods of a unittest.TestCase subclass.
Given a module patch_me_not.py
# patch_me_not.py
def patch_me(at):
print('I am not patched at {}.'.format(at))
def patch_me_not(at):
patch_me(at)
The following script produces more output that I would expect.
# main.py
import unittest
from unittest.mock import patch
from patch_me_not import patch_me_not
#patch('patch_me_not.patch_me', lambda x: None)
class PatchMeNotTests(unittest.TestCase):
#classmethod
def setUpClass(cls):
print('I am the setUpClass.')
patch_me_not('setUpClass')
def setUp(self):
print('I am the setUp.')
patch_me_not('setUp')
def test_print(self):
print('I am the test')
patch_me_not('test_print')
if __name__ == '__main__':
unittest.main()
The test script output is
I am the setUpClass.
I am not patched at setUpClass.
I am the setUp.
I am not patched at setUp.
I am the test
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Where I would not expect the two "I am not patched at..." lines in the output if the patch worked in setUp and setUpClass.
How can I get the mock patch to be applied in these methods?
I think you need to do this:
class PatchMeNotTests(unittest.TestCase):
#classmethod
#patch('patch_me_not.patch_me', lambda x: None)
def setUpClass(cls):
print('I am the setUpClass.')
patch_me_not('setUpClass')
#patch('patch_me_not.patch_me', lambda x: None)
def setUp(self):
print('I am the setUp.')
patch_me_not('setUp')
def test_print(self):
print('I am the test')
patch_me_not('test_print')
Patching your test case did not work because when patch is applied to TestCase it patches only test methods or to be more specific: methods that start with a configurable prefix patch.TEST_PREFIX which default value is "test". That's why your solution did not work.
Here is relevant quote from unittest docs
Patch can be used as a TestCase class decorator. It works by
decorating each test method in the class. This reduces the boilerplate
code when your test methods share a common patchings set. patch()
finds tests by looking for method names that start with
patch.TEST_PREFIX. By default, this is 'test', which matches the way
unittest finds tests. You can specify an alternative prefix by setting
patch.TEST_PREFIX.

Avoid passing second param to every unit test using mock.patch

I'm mocking my RpcClient class for all of my unit tests like this:
import unittest2
from mock import patch
#patch('listeners.RpcClient')
class SomeTestCase(unittest2.TestCase):
test_something(self, mock_api):
...
test_something_else(self, mock_api):
...
For most of my tests I don't want to do any assertions using the mock object, all I want to do is patch the class so the RpcClient doesn't attempt to connect and fire requests for each of my tests (I have it hooked up to a post save event on one of my models).
Can I avoid passing in mock_api into every single one of my tests?
I ended up doing the mocking in setUp using patcher.start():
def setUp(self):
self.rpc_patcher = patch('listeners.RpcClient')
self.MockClass = rpc_patcher.start()
def tearDown(self):
self.rpc_patcher.stop()
So I don't have to decorate any of my test cases and don't have to add any extra arguments to my tests.
More info:
http://docs.python.org/dev/library/unittest.mock#patch-methods-start-and-stop
Can you just set a default parameter to mock_api?
def test_something(self, mock_api=None):
...
def test_something_else(self, mock_api=None):
You could make use mock.patch as a context manager for when you call the SUT. Something like:
import unittest2
from mock import patch
class SomeTestCase(unittest2.TestCase):
def call_sut(self, *args, **kwargs):
with mock.patch('listeners.RpcClient'):
# call SUT
def test_something(self):
self.call_sut()
# make assertions
def test_something_else(self):
self.call_sut()
# make assertions

How to call setup once for all tests and teardown after all are finished

I have a bunch of tests written using pytest. There are all under a directory dir. For example:
dir/test_base.py
dir/test_something.py
dir/test_something2.py
...
The simplified version of code in them is as follows:
test_base.py
import pytest
class TestBase:
def setup_module(module):
assert False
def teardown_module(module):
assert False
test_something.py
import pytest
from test_base import TestBase
class TestSomething(TestBase):
def test_dummy():
pass
test_something2.py
import pytest
from test_base import TestBase
class TestSomethingElse(TestBase):
def test_dummy2():
pass
All my test_something*.py files extend the base class in test_base.py. Now I wrote setup_module(module) and teardown_module(module) methods in test_base.py. I was expecting the setup_module to be called once for all tests, and teardown_module() to be called at the end, once all tests are finished.
But the functions don’t seem to be getting called? Do I need some decorators for this to work?
The OP's requirement was for setup and teardown each to execute only once, not one time per module. This can be accomplished with a combination of a conftest.py file, #pytest.fixture(scope="session") and passing the fixture name to each test function.
These are described in the Pytest fixtures documentation
Here's an example:
conftest.py
import pytest
#pytest.fixture(scope="session")
def my_setup(request):
print '\nDoing setup'
def fin():
print ("\nDoing teardown")
request.addfinalizer(fin)
test_something.py
def test_dummy(my_setup):
print '\ntest_dummy'
test_something2.py
def test_dummy2(my_setup):
print '\ntest_dummy2'
def test_dummy3(my_setup):
print '\ntest_dummy3'
The output when you run py.test -s:
collected 3 items
test_something.py
Doing setup
test_dummy
.
test_something2.py
test_dummy2
.
test_dummy3
.
Doing teardown
The name conftest.py matters: you can't give this file a different name and expect Pytest to find it as a source of fixtures.
Setting scope="session" is important. Otherwise setup and teardown will be repeated for each test module.
If you'd prefer not to pass the fixture name my_setup as an argument to each test function, you can place test functions inside a class and apply the pytest.mark.usefixtures decorator to the class.
Put setup_module and teardown_module outside of a class on module level. Then add your class with your tests.
def setup_module(module):
"""..."""
def teardown_module(module):
"""..."""
class TestSomething:
def test_dummy(self):
"""do some tests"""
For more info refer to this article.
setup_module/teardown_module are called for the module where the eventual (derived) tests are defined. This also allows to customize the setup. If you only ever have one setup_module you can put it to test_base.py and import it from the other places. HTH.
First of all it is a good practice to put all tests in a module called "tests":
<product>
...
tests/
__init__.py
test_something.py
Secondly I think you should use setup_class/teardown_class methods in your base class:
import unittest
class MyBase(unittest.TestCase):
#classmethod
def setup_class(cls):
...
#classmethod
def teardown_class(cls):
...
More info: http://pytest.org/latest/xunit_setup.html

Categories