Unit Test with Mock and Multiple Functions - python

I cannot instantiate an object because it is an abstract class, so I have to use mocking in order to test my code.
I have been told this is best done by creating a new mock class.
class MockMyClass(MyClass):
def my_first_function(...):
The idea is that I then instantiate a MockMyClass object, where I can test private function in that.
I have read the python guide and researched other stack questions. Here, the theory behind mock has been well explained. Unfortunately, I am still lost with how mocking can be used in a large unittest for multiple functions. For instance:
If I have a class, from which other classes in the main code inherit functions from. This can take the form:
class SharedFunctions(AnotherClass):
first_function():
#do some important calculations to generate stuff.#
self.stuff = first_function_attribute_stuff
return returned_first_stuff
second_functions(returned_stuff)
returned_second_stuff = self.stuff + returned_first_stuff
return returned_second_stuff
and where the class SharedFunctions also inherits from another class (noting the abstract method) of the form:
class AnotherClass():
#abc.abstractmethod
def one_important_universal_function(...):
pass
I have tried to construct a unittest for the SharedFunctions piece of code.
This is what I have tried so far:
class MockSharedFunctions(SharedFunctions):
def first_function(...):
self.stuff = some value
returned_first_stuff = given some other value
return returned_first_stuff
def second_function
returned_second_stuff = another value.
return returned_second_stuff
class TestSharedFunctions(unittest.TestCase):
def test_first_function(self):
# insert code #
self.assertTrue(True)
def test_second_function(self):
# insert code #
self.assetEqual(output, expected)
self.assertTrue(True)
if __name__ == "__main__":
unittest.main()
Where insert code has been a number of various attempts to use mocking. However, I have not come across a clear example of how mock functions can be used to replace other functions, or a confirmation that this will work.
Thank you for any help.

A common issue is too over complicate the use of mocking functions. You can almost treat them like another class method. In your case, the abstractmethod decorator is probably generating the confusion.
This is something close to what you might need.
class MockSharedFunctions(SharedFunctions):
def one_important_universal_function(**args):
return 0
class TestSharedFunctions(unittest.TestCase):
def test_first_function(self):
mock = MockSharedFunctions()
mock_output = firstfunction(**args)
mock_stuff = mock.stuff
self.assertTrue(True)
self.assetEqual(mock_output, expected)
self.assetEqual(mock_stuff, expected)
def test_second_function(self):
mock = MockSharedFunctions()
mock.stuff = some_value
mock_output = second_function(**args)
self.assetEqual(mock_output, expected)
self.assertTrue(True)
if __name__ == "__main__":
unittest.main()
Here, in the MockSharedFunctions you are already inheriting SharedFunctions. As one_important_universal_function is an abstract method, it needs to be defined.

Related

Mock a subset of a Python class's methods and properties

I'm using the mock Python module for performing my tests.
There are times when I'm mocking a class, however I just want to mock some of its methods and properties, and not all of them.
Suppose the following scenario:
# module.py
class SomeClass:
def some_method(self):
return 100
def another_method(self):
return 500
# test.py
class Tests(unittest.TestCase):
#patch('module.SomeClass')
def test_some_operation(self, some_class_mock):
some_class_instance = some_class_mock.return_value
# I'm mocking only the some_method method.
some_class_instance.some_method.return_value = 25
# This is ok, the specific method I mocked returns the value I wished.
self.assertEquals(
25,
SomeClass().some_method()
)
# However, another_method, which I didn't mock, returns a MagicMock instance
# instead of the original value 500
self.assertEquals(
500,
SomeClass().another_method()
)
On the code above, once I patch the SomeClass class, calls to methods whose return_values
I didn't exlicitely set will return MagicMock objects.
My question is: How can I mock only some of a class methods but keep others intact?
There are two ways I can think of, but none of them are really good.
One way is to set the mock's method to the original class method, like this:
some_class_instance.another_method = SomeClass.another_method
This is not really desirable because the class may have a lot of methods and properties to
"unmock".
Another way is to patch each method I want explicitly, such as:
#patch('module.SomeClass.some_method')
def test_some_operation(self, some_method_mock):
But this doesn't really work if I want to mock the class itself, for mocking calls to the
initializer for example. The code below would override all SomeClass's methods anyway.
#patch('module.SomeClass.some_method')
#patch('module.SomeClass')
def test_some_operation(self, some_class_mock, some_method_mock):
Here is a more specific example:
class Order:
def process_event(self, event, data):
if event == 'event_a':
return self.process_event_a(data)
elif event == 'event_b':
return self.process_event_b(data)
else:
return None
def process_event_a(self, data):
# do something with data
def process_event_b(self, data):
# do something different with data
In this case, I have a general method process_event which calls a specific processing event depending on the supplied event.
I would like to test only the method process_event. I just want to know if the proper specific event is called depending on the event I supply.
So, in my test case what I want to do is to mock just process_event_a and process_event_b, call the original process_event with specific parameters, and then assert either process_event_a or process_event_b were called with the proper parameters.
Instead of patching the whole class, you must patch the object. Namely, make an instance of your class, then, patch the methods of that instance.
Note that you can also use the decorator #patch.object instead of my approach.
class SomeClass:
def some_method(self):
return 100
def another_method(self):
return 500
In your test.py
from unittest import mock
class Tests(unittest.TestCase):
def test_some_operation(self):
some_class_instance = SomeClass()
# I'm mocking only the some_method method.
with mock.patch.object(some_class_instance, 'some_method', return_value=25) as cm:
# This is gonna be ok
self.assertEquals(
25,
SomeClass().some_method()
)
# The other methods work as they were supposed to.
self.assertEquals(
500,
SomeClass().another_method()
)

How should a Python (unittest) superclass method reference a variable in its calling subclass?

I am simply not experienced enough in Python OO programming to know how this is done: If I have several classes that are subclasses of a unittest.TestCase subclass. How should the superclass' methods reference variables of the subclasses when the latter call these methods? Let me try to illustrate it with this, probably wrong, example:
import unittest
class TestSuper(unittest.TestCase):
def test_method(self):
# do something, e.g.
pass
class TestSub1(TestSuper):
def setUp(self):
self.some_parameter = 1
class TestSub2(TestSuper):
def setUp(self):
self.some_parameter = 2
if __name__ == '__main__':
unittest.main()
Now, I cannot figure out how to correcty reference TestSub1.parameter or TestSub2.parameter, respectively, when TestSuper.test_method is called from the subclasses.
I am inspired by https://stackoverflow.com/a/25695512/865169, but here I am trying achieve having multiple test cases that do the same but only differ in their set-up. I can of course achieve all this by just copy-pasting my test case definitions, but I find that bad coding practice.
Just use self.some_parameter; self is always bound to the right instance here.
However, take into account that unittest.main() will run test_method on *all three test cases (the baseclass included), so you'll have to provide a default value for some_parameter on the base class.
If you don't want TestSuper to be seen as a test case, don't inherit from unittest.TestCase. Use it as a mix-in instead:
import unittest
class TestMixin(object):
def test_method(self):
# do something, e.g.
pass
class TestSub1(unittest.TestCase, TestMixin):
def setUp(self):
self.some_parameter = 1
class TestSub2(unittest.TestCase, TestMixin):
def setUp(self):
self.some_parameter = 2
if __name__ == '__main__':
unittest.main()
Now unittest.main() will only find two test cases (TestSub1 and TestSub2) and run test_method only on those cases.

Mock a class and a class method in python unit tests

I'm using python's unittest.mock to do some testing in a Django app. I want to check that a class is called, and that a method on its instance is also called.
For example, given this simplified example code:
# In project/app.py
def do_something():
obj = MyClass(name='bob')
return obj.my_method(num=10)
And this test to check what's happening:
# In tests/test_stuff.py
#patch('project.app.MyClass')
def test_it(self, my_class):
do_something()
my_class.assert_called_once_with(name='bob')
my_class.my_method.assert_called_once_with(num=10)
The test successfully says that my_class is called, but says my_class.my_method isn't called. I know I'm missing something - mocking a method on the mocked class? - but I'm not sure what or how to make it work.
Your second mock assertion needs to test that you are calling my_method on the instance, not on the class itself.
Call the mock object like this,
my_class().my_method.assert_called_once_with(num=10)
^^
A small refactoring suggestion for your unittests to help with other instance methods you might come across in your tests. Instead of mocking your class in each method, you can set this all up in the setUp method. That way, with the class mocked out and creating a mock object from that class, you can now simply use that object as many times as you want, testing all the methods in your class.
To help illustrate this, I put together the following example. Comments in-line:
class MyTest(unittest.TestCase):
def setUp(self):
# patch the class
self.patcher = patch('your_module.MyClass')
self.my_class = self.patcher.start()
# create your mock object
self.mock_stuff_obj = Mock()
# When your real class is called, return value will be the mock_obj
self.my_class.return_value = self.mock_stuff_obj
def test_it(self):
do_something()
# assert your stuff here
self.my_class.assert_called_once_with(name='bob')
self.mock_stuff_obj.my_method.assert_called_once_with(num=10)
# stop the patcher in the tearDown
def tearDown(self):
self.patcher.stop()
To provide some insight on how this is put together, inside the setUp method we will provide functionality to apply the patch across multiple methods as explained in the docs here.
The patching is done in these two lines:
# patch the class
self.patcher = patch('your_module.MyClass')
self.my_class = self.patcher.start()
Finally, the mock object is created here:
# create your mock object
self.mock_stuff_obj = Mock()
self.my_class.return_value = self.mock_stuff_obj
Now, all your test methods can simply use self.my_class and self.mock_stuff_obj in all your calls.
This line
my_class.my_method.assert_called_once_with(num=10)
will work if my_method is a class method.
Is it the case?
Otherwise, if my_method is just an normal instance method, then you will need to refactor the function do_something to get hold of the instance variable obj
e.g.
def do_something():
obj = MyClass(name='bob')
return obj, obj.my_method(num=10)
# In tests/test_stuff.py
#patch('project.app.MyClass')
def test_it(self, my_class):
obj, _ = do_something()
my_class.assert_called_once_with(name='bob')
obj.my_method.assert_called_once_with(num=10)

How to pass a function I want to check as a parameter to unittest.TestCase in Python 3.x

For example, I have functions that are working with strings. And I would like to create an unittest, which is checking the functions. Sometimes, tests are identical, so I was thinking for creating BaseCase, which is do similar jobs for all functions. And function is an argument for this BaseCase. How can I do it? I came up with something like this...
class BaseTestCase (unittest.TestCase):
def setUp(self):
self.newMethod = None
def testMethod (self):
if self.newMethod:
self.assertEqual (1, self.newMethod())
def someF():
return 1
class SomeTestCase (BaseTestCase):
def setUp(self):
self.newMethod = someF
if __name__ == "__main__":
unittest.main()
It is working as I need, but it fire one extra test (with all ok in it) for test method. Is it possible to do the same job via _init__ and super()?

Persist variable changes between tests in unittest?

How do I persist changes made within the same object inheriting from TestCase in unitttest?
from unittest import TestCase, main as unittest_main
class TestSimpleFoo(TestCase):
foo = 'bar'
def setUp(self):
pass
def test_a(self):
self.assertEqual(self.foo, 'bar')
self.foo = 'can'
def test_f(self):
self.assertEqual(self.foo, 'can')
if __name__ == '__main__':
unittest_main()
I.e.: I want those two tests above to pass
As some comments have echoed, structuring your tests in this manner is probably a design flaw in the tests themselves and you should consider restructuring them. However, if you want to do this and rely on the fact that the test runner you are using executes them in an alphabetical (seemingly) order then I suggest the following.
Similar to what #Matthias was saying but I would do one thing differently for the cases where you may decide to inherit from the class at a later date.
from unittest import TestCase, main as unittest_main
class TestSimpleFoo(TestCase):
foo = 'bar'
def setUp(self):
pass
def test_a(self):
self.assertEqual(self.__class__.foo, 'bar')
self.__class__.foo = 'can'
def test_f(self):
self.assertEqual(self.__class__.foo, 'can')
if __name__ == '__main__':
unittest_main()
The difference between this answer and #Matthias's answer you accepted is the explicit declaration of the class versus the lookup of said class reference.
TestSimpleFoo vs self.__class__
I prefer the dynamicness so I can inherit the tests later and run both test classes back to back and not have any cross over between the two. Because if you would choose to inherit from this class, explicitly naming the class reference would cause both test classes to run against that reference rather than their own respective classes.
I like your own answer for the simplicity of it, but if you want to keep distinct unit tests:
Apparently unittest runs separate tests with fresh instances of the TestCase. Well, just bind the objects to be persisted to something else but self. For example:
from unittest import TestCase, main as unittest_main
class TestSimpleFoo(TestCase):
def setUp(self):
pass
def test_a(self):
TestSimpleFoo.foo = 'can'
def test_f(self):
self.assertEqual(TestSimpleFoo.foo, 'can')
if __name__ == '__main__':
unittest_main()
You might be interesed in setUpClass and tearDownClass too:
https://docs.python.org/3/library/unittest.html#setupclass-and-teardownclass
Also take care about the execution order of your unit tests:
https://docs.python.org/2/library/unittest.html#unittest.TestLoader.sortTestMethodsUsing
Couldn't figure it out; so ended up hacking it out with multiple non test_ prefixed functions:
def test_password_credentials_grant(self):
for user in self.user_mocks:
self.register(user)
self.login(user)
self.access_token(user, self.assertEqual) # Ensures access_token is generated+valid
self.logout(user)
self.access_token(user, self.assertNotEqual) # Ensures access_token is now invalid
self.unregister(user)

Categories

Resources