How to mock properly classes and methods from imported modules - python

I'm trying to create some unitary tests for a method that needs to instantiate some objects using classes from external modules that I don't want to use, as they need arguments that I can't pass at object initialization.
For example, imagine that the code I want to test has the following structure:
from module_one import Class_1
from module_two import Class_2
class MyMainClass()
def method_to_be_tested(self, stuff):
var_one = Class_1(stuff.data)
self.var_two = Class_2(var_one.data)
print("The var is %s" % self.var_two.attribute)
stuff is a complex argument, with several attributes that I can't mock.
This is what I have tried in my test method, using patch from unittest.mock (but it didn't work):
#patch('module_two.Class_2')
#patch('module_one.Class_1')
def test_method(self, mocked_class1, mocked_class2):
stuff = mock()
mocked_class1.return_value = 'some_stuff_that_i_dont_want'
mock_class2 = mock()
mock_class2.attribute = 'what_i_want_to_get'
mocked_class2 = mock_class2
mymainclass.method_to_be_tested(stuff)
assertEqual('what_i_want_to_get', mymainclass.var_two.attribute)
It seems that the patch or something isn't working, as it throws an error telling me that str object has no attribute data, referring to var_one when var_one.data is used as an argument for Class2.
What I want is to pass any argument to Class2 and get always what I defined in mock_class2.
Edit: mock() is imported from mockito module, but maybe I don't need this to achieve what I need.

You can certainly do that without mockito. Assuming that MyMainClass is defined in module mainmodule:
import unittest.mock as mock
class TestMyMainClass(unittest.TestCase):
#mock.patch('mainmodule.Class_2')
#mock.patch('mainmodule.Class_1')
def test_method(self, mocked_class1, mocked_class2):
stuff = mock.Mock()
mocked_class2_instance = mock.Mock()
mocked_class2_instance.attribute = 'what_i_want_to_get'
mocked_class2.return_value = mocked_class2_instance
mymainclass = MyMainClass()
mymainclass.method_to_be_tested(stuff)
self.assertEqual('what_i_want_to_get', mymainclass.var_two.attribute)
if __name__ == '__main__':
unittest.main()
Now if you think this test sucks, I have to agree.
You have to patch Class_1, yet it doesn't even appear in the test.
It is not obvious why Class_2 is relevant at all unless you are familiar with the internal implementation details of the method under test. Actually you are testing implementation details instead of (or in addition to) observable behavior, which is usually bad.
The behavior of the method depends (hopefully) on stuff.data but the test ignores that and will happily pass whatever it is. An yet you need a stuff object with a data attribute even if you don't care about what it is.
The main problem here is that by instantiating Class_1 and Class_2 inside the method you are tightly coupling the three pieces of code but now you want to test them in isolation. That's going to be hard and will probably lead to fragile and hard to read tests.
If you really want your code to be like that I suggest you test the method without mocking neither Class_1 nor Class_2. Now if you say you don't want to do that because stuff (or better, stuff.data; why are you passing the whole think when you only need the data?) is hard to mock or instantiate, I would guess that there is a problem with your design, but that is as far as I can get from your contrived example.

Related

Python mocking the right class/import

I am trying to implement unittests for my python program. The problem is, that my program is using several imported classes, which I would like to replace by a mocked object/class to verify single functions/methods.
I do not get any errors with my mocked class. But it appears that the mock itself didn't replace the object I wanted to replace.
This is basically my structure:
First the class I want to mock. Might look like that:
class ToMock():
def getSomething(self):
return "something"
The class I want to test looks like this:
from x.y import ToMock
class ClassToTest():
def __init__(self):
self.obj = ToMock()
def returnStuff():
return self.obj.getSomething()
As you can imagine, I want to test the returnStuff method. Therfore I want to mock .getSomething, or better said the whole ToMock object.
The unittest should therefore test the ClassToTest with the mocked ToMock class. I tried several mock.patch variants, but I couldn't get it to run/test properly.
import unittest
from unittest import mock
from a.b import ClassToTest
class TestObject(unittest.TestCase):
def setUp(self):
with mock.patch('x.y.ToMock') as mock_obj:
mock_obj.return_value.getSomething().return_value="mocked return value"
self.test_class = ClassToTest()
result = self.test_class.returnStuff() # This should return now 'mocked return value', I guess?
mock_obj.return_value.getSomething.assert_called_once_with("")
The problem I face is, that the self.test_class.returnStuff() is not "calling" the mocked object, but imports the real class etc. and therefore I am running into timeouts, or similar stuff.
I am sure, that I provide the wrong path for the object which should be mocked. Perhaps someone can hint me into the right direction.
Thanks
-GreNait
The issue is that you are not patching in the correct place. You are patching where the object is defined as opposed to where it is looked up.
a.py
-> Defines ToMock
b.py
-> from a import ToMock
-> some_function/class instantiates ToMock
In your code shown you are patching a.ToMock however you should be patching b.ToMock. That is why it is not running your mock object when testing. You can read more about where to patch here.

python testing as an independent script

I have taken a below code from a large code repository of interdependent modules. I want to run this as an independent unit for testing.
In below while calling db_obj.create_connection() i have to pass the value of debug_obj which means I have to import all the dependent implementation of that which I do not want.
However, if I pass None as db_obj.create_connection(None) then it will fail at debug_obj.info('inside create_connection') due to attribute error. Unless I go and disable debug_obj.info() wherever used.
What could be the best possible way to handle such a situation where you wanted to disable you dependent library codes just for your unit testing without commenting its callings.
import pyodbc
class DBOperations(object):
def __init__(self,db_params):
self.db_params=db_params
def create_connection(self, debug_obj):
debug_obj.info('inside create_connection')
mycode_to_run_create_connection
if __name__ == '__main__':
db_obj = DBOperations(db_params ='param_for_db_connection')
db_obj.create_connection(None)
It looks like using the mock library would be a good way of dealing with a situation like yours.
In your case, a Mock or MagicMock should do the trick.
In the same place where you are instantiating the DBOperations class, you could mock the debug_obj:
from unittest.mock import Mock
...
...
if __name__ == '__main__':
debug_obj = Mock()
db_obj = DBOperations(db_params ='param_for_db_connection')
db_obj.create_connection(debug_obj)
Once the Mock object has been instantiated, attributes can be assigned to it, for example:
debug_obj = Mock()
debug.info = print
debug.info('test')
# >>> prints the word test to the stdout

When should I use `autospec=True` with the mock library?

When should I use autospec=True when using mock.patch and its variants?
On one hand, this article warns us to always use autospec=True:
... you should always use the create_autospec method and the autospec parameter with the #patch and #patch.object decorators.
On the other hand, autospec has serious drawbacks and limits, as explained in idjaw's answer to this question.
So my question is: when should I use autospec=True or create_autospec, and when should I not use it?
I fear not using autospec may result in tests not breaking when they really should break, as described in the mentioned article. However autospec has its drawbacks. How should I act?
I can understand the motivation to suggest the enforcing of using autospec.
Maybe the following could help provide more clarity on what you get and don't get with autospec.
In short, using autospec ensures that the attributes you use in your mock are in fact part of the class you are mocking.
So, with the example below, I'll illustrate how a test will pass when technically you might not want it to:
Take this simple example we will test:
class Foo:
def __init__(self, x):
self.x = x
class Bar:
def __init__(self):
self.y = 4
self.c = Foo('potato')
And the test code:
class TestAutoSpec(unittest.TestCase):
#patch('some_module.Foo')
def test_autospec(self, mock_foo_class):
mock_foo_obj = mock_foo_class.return_value
bar_obj = some_module.Bar()
self.assertTrue(hasattr(bar_obj.c, 'you_should_fail'))
Now, if you look back at the Foo class, you will clearly see you_should_fail is clearly not an attribute in Foo. However, if you run this test code, it will in fact pass. Which is very misleading.
This is because if an attribute does not exist in a MagicMock, it will still be of type MagicMock. If you print type(bar_obj.c.you_should_fail) in that test, you will end up getting:
<class 'unittest.mock.MagicMock'>
This will certainly cause the hasattr test to pass. If you run the above test again, except change your patch to be: #patch('some_module.Foo', autospec=True), it will fail as it should.
Now, to write a successful test for this and still use autospec=True, you simply create the attribute in your mock testing as needed. Remember, the reason this is needed, is because autospec cannot know about the attributes created dynamically, i.e. in the __init__ when you create an instance.
So, the autospec way to do this, would be:
class TestAutoSpec(unittest.TestCase):
#patch('some_module.Foo', autospec=True)
def test_autospec(self, mock_foo_class):
mock_foo_obj = mock_foo_class.return_value
# create the attribute you need from mocked Foo
mock_foo_obj.x = "potato"
bar_obj = some_module.Bar()
self.assertEqual(bar_obj.c.x, 'potato')
self.assertFalse(hasattr(bar_obj.c, 'poof'))
Now, your test will successfully pass at validating your x attribute, while also validating that you don't have some bogus attribute that does not exist in your real Foo class.
Here is also another explanation by Martijn Pieters, that does not necessarily directly answer your question, but gives a very good example and explanation of using autospec that can help further your understanding:
https://stackoverflow.com/a/31710001/1832539

What's the right way of unit-testing a python class?

I'm not sure what's the best way to build independent unit tests on methods of a python class. Here is a trivial example:
class MyClass(object):
def __init__(self, data):
self.data = data
def myMethod(self):
#do something
return True
I'm building unit tests like this:
class TestMyClass(unittest.TestCase):
def test_init(self):
mydata = mock.Mock()
c = MyClass(mydata)
self.assertEqual(c.data, mydata)
def test_myMethod(self):
mydata = mock.Mock()
c = MyClass(mydata)
self.assertTrue(c.myMethod())
OK, this is very trivial... but the point is: test_myMethod() depends on __init__ because I have to instantiate the object itself: it isn't really isolated. The only way that has come to my mind to solve this issue is mocking the object itself... but how can I test a real method on a mocked object?
I know that in real world a perfect isolation could be impossible, but I'm curious to know if there are better ways.
It comes down to what you want to test and in how much granularity. For example, what exactly is the assertion that c.data equals the data you have just assigned in the previous line actually test? It tests a single line of practical code, and it tests a rather unimportant implementation detail. This is probably much too granular to bother.
You want to ensure that your object behaves as it should overall. That means you want to test that you can instantiate it, and then assert that
if you instantiate it with data A and then call method B the result will be C
if you instantiate it with data D and then call method B the result will be E
if you try to instantiate it with incorrect data that an error is thrown
Your tests should focus on object behaviour, not on testing implementation minutiae. Write your tests so you could drop in an alternative object implementing the same behaviour in a different way without breaking your tests. You are writing that class to get some specified tasks done; test that your implementation can get those tasks done, not how it gets them done.
Exempli gratia:
def test_worksWithDataA(self):
c = MyClass({'some': 'data'})
self.assertTrue(c.myMethod())
def test_worksWithDataB(self):
c = MyClass({'other': 'data'})
self.assertFalse(c.myMethod())
def test_rejectsDataC(self):
with self.assertRaises(SomeException):
MyClass(None)
You only need to mock something if you need to pass a dependency, but that dependency is too complex to instantiate for the sake of the test. As long as your data is just a dict, there's no need to mock it. If your data is a complex type on its own, then you may want to mock it in order to test MyClass, without getting tangled up in having to test that other class at the same time. When mocking, again, mock the expected behaviour of that class; as a rule of thumb, all the behaviour you're asserting your class has using unit tests can be mocked elsewhere.

overloading __init__ of unittest.testcase

I want to add two variables to my subclass which is inherited from unittest.testcase
like I have:
import unittest
class mrp_repair_test_case(unittest.TestCase):
def __init__(self, a=None, b=None, methodName=['runTest']):
unittest.TestCase.__init__(self)
self.a= a
self.b = b
def test1(self):
..........
.......
def runtest()
mrp_repair_test_case(a=10,b=20)
suite = unittest.TestLoader().loadTestsFromTestCase(mrp_repair_test_case)
res = unittest.TextTestRunner(stream=out,verbosity=2).run(suite)
how can I acvhieve this:
I am getting this error:
ValueError: no such test method in ****<class 'mrp_repair.unit_test.test.mrp_repair_test_case'>:**** runTest
thanks
At first glance, it looks like you need to create an instance of mrp_repair_test_case. Your current line:
mrp_repair_test_case(a=10,b=20)
doesn't actually do anything.
Try (not tested):
def runtest():
m = mrp_repair_test_case(a=10, b=20)
suite = unittest.TestLoader().loadsTestsFromTestCase(m)
res = unittest.TextTestRunner(stream=out, verbosity=2).run(suite)
This assumes you've set up 'out' as a stream already.
Edit:
By the way, is there any reason you're not using a setUp method to set these values? That would be normal best practice. Looking at the documentation of loadTestsFromTestCase it looks like it will only accept the Class itself not an instance of it, which would mean you're rather working against the design of the unittest module.
Edit 2:
In response to your further information, I would actually set your uid and cursor values seperately at module level before calling the tests. I'm not a huge fan of globals normally, but if I'm understanding you correctly these values will be A) read-only B) always the same for the same customer which avoids most of the normal pitfalls in using them.
Edit 3:
To answer your edit, if you really want to use __init__ you probably can, but you will have to roll your own loadsTestsFromTestCase alternative, and possibly your own TestSuite (you'll have to check the internals of how it works). As I said above, you'll be working against the existing design of the module - to the extent that if you decide to do your testing that way it might be easier to roll your own solution completely from scratch than use unittest. Amend: just checked, you'd definately have to roll your own version of TestSuite, as the existing one creates a new instance of the TestCaseClass for each test.

Categories