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 _ :
...
Related
I have a fixture mocking an external library like so, using pytest-mock, which is a wrapper around unittest.mock.
# client.py
import Test as TestLibrary
class LibraryName():
def get_client():
return TestLibrary.Library()
# library_service.py
def using_library():
'''
Edited note: Library().attribute behind the scenes is set to
self.attribute = Attribute()
so this may be affecting the mocking
'''
client = LibraryName.get_client()
return client.attribute.method()
# conftest.py
#pytest.fixture
def library_client_mock(mocker):
import Test as TestLibrary
return mocker.patch.object(TestLibrary, 'Library')
# test_library_service.py
def test_library_method(library_client_mock):
result = using_library()
I can mock a return value like so:
def test_library_method(library_client_mock):
library_client_mock.return_value.attribute.return_value.method.return_value = "test"
result = using_library()
assert result == "test"
but I can't mock throwing an Exception with side_effect
def test_library_method(library_client_mock):
library_client_mock.return_value.attribute.return_value.method.side_effect = TypeError # doesn't work
library_client_mock.return_value.attribute.return_value.method.side_effect = TypeError() # doesn't work
attrs = { 'attribute.method.side_effect': TypeError }
library_client_mock.configure_mock(**attrs) # doesn't work
with pytest.raises(TypeError):
using_library() # fails assertion
what I missing here?
These are the errors in your code:
Change:
library_client_mock.return_value.attribute.return_value.method.return_value = "test"
To:
library_client_mock.return_value.attribute.method.return_value = "test"
Change:
library_client_mock.return_value.attribute.return_value.method.side_effect = TypeError
To:
library_client_mock.return_value.attribute.method.side_effect = TypeError
Explanation
The .return_value must only be used for callable objects e.g. a function as documented:
return_value
Set this to configure the value returned by calling the mock:
>>> mock = Mock()
>>> mock.return_value = 'fish'
>>> mock()
'fish'
Thus, you can use .return_value only for the following:
TestLibrary.Library()
TestLibrary.Library().attribute.method()
But not for:
TestLibrary.Library().attribute
Because .attribute is not a callable e.g. TestLibrary.Library().attribute().
Warning
The way you are patching Library is via its source location at Test.Library (or aliased as TestLibrary.Library). specifically via:
import Test as TestLibrary
return mocker.patch.object(TestLibrary, 'Library')
It works currently because the way you import and use it is via the root path.
# client.py
import Test as TestLibrary
...
return TestLibrary.Library()
...
But if we change the way we imported that library and imported a local version to client.py:
# client.py
from Test import Library # Instead of <import Test as TestLibrary>
...
return Library() # Instead of <TestLibrary.Library()>
...
It will now fail. Ideally, you should patch the specific name that is used by the system under test, which here is client.Library.
import client
return mocker.patch.object(client, 'Library')
Unless you are sure that all files that will use the library will import only the root and not a local version.
#Niel Godfrey Ponciano set me on the right path with this syntax for the side_effect
library_client_mock.return_value.attribute.method.side_effect = TypeError
but it wasn't enough.
In
# conftest.py
#pytest.fixture
def library_client_mock(mocker):
import Test as TestLibrary
return mocker.patch.object(TestLibrary, 'Library')
I had to add an extra mock:
# conftest.py
#pytest.fixture
def library_client_mock(mocker):
import Test as TestLibrary
mock_library_client = mocker.patch.object(TestLibrary, 'Library')
# option 1
mock_attribute = Mock()
# option 2, path to Library.attribute = Attribute()
mock_attribute = mocker.patch.object(TestLibrary.services, 'Attribute', autospec=True)
mock_library_client.attach_mock(mock_attribute, "attribute")
return mock_library_client
and then both of the following statements worked as expected. Although I am not sure why return_value works out of the box without an attached mock, but side_effect does not.
# return_value set correctly
# NOTE return_value needed after each
library_client_mock.return_value.attribute.return_value.method.return_value = "test"
# side_effect set correctly
# NOTE return_value not needed after "attribute"
library_client_mock.return_value.attribute.method.side_effect = TypeError
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.
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
Is there a way to tell nose not to test a particular function foo() enclosed in a file containing other functions that need to be tested ?
def foo():
'''Was the function does.
:Example:
>>> # I don't wan't to test this code
>>> # because it uses imports
>>> # of some packages not declared as dependencies
'''
Best
You can raise SkipTest:
from nose.plugins.skip import SkipTest
def test_that_only_works_when_certain_module_is_available():
if module is not available:
raise SkipTest("Test %s is skipped" % func.__name__)
or use unittest.skip decorator:
import unittest
#unittest.skip("temporarily disabled")
class MyTestCase(unittest.TestCase):
...
alternatively, if this isn't even a test function but is incorrectly detected as test function, and you don't want the function to appear in test report as skipped test, you can mark the function with nottest decorator:
from nose.tools import nottest
#nottest
def test_but_not_really_test()
...
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.