How to use a pytest fixture to instantiate a object under test? - object

It appears that fixtures should be used to instantiate an object under test for pytest, especially if it is used by several test_ functions. However, after trying to adapt examples given in the pytest doc, I cannot get the following to work.
import pytest
...
#pytest.fixture
def newMyClass():
obj = MyClass(1,2)
...
def test_aMethod(newMyClass):
objectUnderTest = newMyClass.obj
...
There are no complaints about the fixture or the constructor, but then I receive the pytest error
def test_aMethod(newMyClass):
> objectUnderTest = newMyClass.obj()
E AttributeError: 'NoneType' object has no attribute 'obj'
If fixtures can be used for this, how should that be coded?

To clean up #hoefling's answer, you need to instantiate your class directly and return that instance. Check out this code if you're looking for a cleaned up version.
import pytest
class MyClass():
def __init__(self, obj, foo):
self.obj = obj
self.foo = foo
#pytest.fixture
def newMyClass():
myClassInstance = MyClass(1,2)
return myClassInstance
def test_aMethod(newMyClass):
objectUnderTest = newMyClass.obj
assert objectUnderTest

Related

python mock - patching a method with itself - missing 1 required positional argument

I would like to patch a method to able to test whether it was called or not, but in the same time I dont want to loose the functionality so the idea was to patch the method with itself:
import unittest
from unittest.mock import Mock, patch
class MyClass():
def foo(self, num):
return num + 2
class myTestClass(unittest.TestCase):
#patch.object(MyClass,'foo', Mock(wraps=MyClass.foo))
def test_foo(self):
my_class = MyClass()
result = my_class.foo(2)
my_class.foo.assert_called_once_with(2)
self.assertEqual(result, 4)
During execution I am getting the following error:
File "/usr/lib64/python3.6/unittest/mock.py", line 1014, in _mock_call
return self._mock_wraps(*args, **kwargs)
TypeError: foo() missing 1 required positional argument: 'num'
Is this kind of patching possible in this way?
There is probably a workaround described here
I see several problems here:
You are patching the class, not the object; but testing the object, not the class.
You are wrapping with a Mock which I feel that it is wrong.
And a minor one: you have named the object that you want to test as my_class, which can be misread easily.
I think that a right solution for your algorithm can be:
import unittest
from unittest.mock import Mock, patch
class MyClass():
def foo(self, num):
return num + 2
class myTestClass(unittest.TestCase):
#patch.object(MyClass,'foo', wraps=MyClass.foo)
def test_foo(self, mocked):
obj = MyClass()
result = MyClass.foo(obj, 2)
MyClass.foo.assert_called_once_with(obj, 2)
self.assertEqual(result, 4)
if __name__ == '__main__':
unittest.main()
Note that:
wraps=MyClass.foo instead of wraps=Mock(...).
def test_do(self, mocked) instead of def test_do(self) (mock object is passed to the method).
MyClass.foo(obj, 2) since you have been patched the class, not the object.
MyClass.foo.assert_called_once_with(obj, 2) since you must check the class method (which you have been patched).
Alternatively, you can patch the object like in this test:
def test_foo_object(self):
obj = MyClass()
with patch.object(obj, 'foo', wraps=obj.foo):
result = obj.foo(2)
obj.foo.assert_called_once_with(2)
self.assertEqual(result, 4)

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.

Mocking a class in a Flask API

I have three files
helper.py
class helper:
def __init__(self, out_file):
self.out_file = out_file
def foo(first, second):
# Write data to file
flask_API.py
from helper import helper
#app.route('/', methods=['POST'])
def parse_request():
content = request.get_json()
out_file = #based on timestamp
helper(out_file).foo(content['first'], content['second'])
test_flask.py
import unittest
from unittest.mock import patch
import flask_API
class testFlaskAPI(unittest.TestCase):
def setUp(self):
self.app = flask_API.app.test_client()
self.app.test = True
#patch('flask_API.app.helper', return_value=None)
def test_service(self, mock_helper):
response = self.app.post(base_url, data=json.dumps({"some":"value"}, content_type='application/json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
I am having trouble mocking the helper class. This gives me an error saying
AttributeError: <Flask 'flask_API'> does not have the attribute 'helper'
I read that a class/method needs to be mocked where it is being called instead of where it's defined. Is there something wrong with the way I am patching the class?
In the end the solution turned out to be fairly simple. First there was no need to add app in the #patch decorator. The test just needed #patch('flask_API.helper'). Second, I first needed to return the mock of the class and then mock the function call as well. So the final answer turned out to be
#patch('flask_API.helper')
def test_service(self, mock_helper):
mocking_helper = mock_helper.return_value # mocking the class
mocking_helper.foo.return_value = None
response = self.app.post(base_url, data=json.dumps({"some":"value"}, content_type='application/json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

Python unittest: Mock function in TestCase class

My approach to mock testing functions looks like this:
from unittest import mock, TestCase
from main import my_function
def my_mock(s):
if s == 'hello':
return 'goodbye'
return my_function(s)
class TestMyFunction(TestCase):
#mock.patch('my_function', side_effect=MyMock.my_mock)
def test_my_function(self, mock_get):
s = 'hello'
self.assertEqual('goodbye', my_function(s))
This works. But if I have multiple tests, where my_mock_1 patches test_my_function_1 and my_mock_2 patches test_my_function_2 and so on, the mock definitions are very far from the test definitions and the code becomes hard to read.
Is there a way to get the mock definition closer to the tests they belong to?
What I tried was
class TestMyFunction(TestCase):
#staticmethod
def my_mock_1(s):
...
#mock.patch('my_function', side_effect=my_mock_1)
def test_my_function_1(self, mock_get):
...
#staticmethod
def my_mock_2(s):
...
#mock.patch('my_function', side_effect=my_mock_2)
def test_my_function_2(self, mock_get):
...
...
But this fails with the exception
TypeError: 'staticmethod' object is not an iterator.

python3 mock doesn't work for all paths

The Production file (production_file.py) is:
class MyError(Exception):
pass
class MyClass:
def __init__(self):
self.value = None
def set_value(self, value):
self.value = value
def foo(self):
raise RuntimeError("error!")
class Caller:
def bar(self, smth):
obj = MyClass()
obj.set_value(smth)
try:
obj.foo()
except MyError:
pass
obj.set_value("str2")
obj.foo()
Test file (test.py):
import unittest
from unittest.mock import patch
from unittest.mock import call
from production_file import MyClass, Caller
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
my_class_mock_obj = MyClassMock.return_value
my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
my_class_mock_obj.set_value.assert_has_calls(calls)
if __name__ == '__main__':
unittest.main()
This above works. But if I move the production classes (MyError, MyClass, Caller) into the test file, and update patch to:
with patch('test.MyClass', autospec=MyClass) as MyClassMock:
then the instance method "foo" is no longer mocked.
Does anybody have any idea why that is?
I have also experienced a similar problem with some more complex code, where the production code is in my_package/src/production_file.py while the test is in my_package/tests/test_file.py. Python yields no error for the path, the path is correct, but still the mock doesn't work.
If you are running test.py as __main__ then it is not test.MyClass it would be __main__.MyClass, or in both cases __name__+".MyClass".
I was able to determine that the class used and the class patched were different by adding a print statement:
class Caller:
def bar(self, smth):
print(MyClass) #lets see what we are actually making an instance of...
obj = MyClass()
...
When the patch is applied to the class that this is using you would see something like this:
<MagicMock name='MyClass' spec='MyClass' id='4387629656'>
But when the class in moved into test.py you will see something like:
<class '__main__.MyClass'>
Which indicates:
There was no patching applied to MyClass (at least the one that is used for the test.)
The name of the class that needs to be patched is __main__.MyClass
However It is quite likely that your "more... complicated situation" is not working because of a setup like this:
from production_file import MyClass
class MyError(Exception):
pass
class Caller:
def bar(self, smth):
print(MyClass)
obj = MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...
In this case production_file.MyClass is being patched and MyClass is being imported from production_file so the correct class is being patched but still the output is:
<class 'production_file.MyClass'>
This is because the Class was directly imported to the local namespace, so when the patch is applied to the production_file the local namespace is still unaffected, we can check that the patch was actually applied with:
...
def bar(self, smth):
print(MyClass)
from production_file import MyClass as pf_MyClass
print(pf_MyClass)
...
#output:
<class 'production_file.MyClass'>
<MagicMock name='MyClass' spec='MyClass' id='4387847136'>
If this is the case you just need to import the module, not the class directly. Then once the patch is applied you will be using it right from the file:
import production_file
...
class Caller:
def bar(self, smth):
print(production_file.MyClass)
obj = production_file.MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...

Resources