How to replace default argument in __init__ method in a unittest - python

I have a class under test that exposes an argument in __init__ just for testing. It looks like this
class MyClass(object):
def __init__(self, start_time=None):
if start_time is None:
start_time = time.time()
In my test, I want to pass in start_time using a fake time, so I get a time in setUp() first and then pass it to every test method.
class MyclassTest(TestCase):
def setUp(self):
self.fake_start_time = time.time()
def test_one(self):
x = MyClass(start_time=self.fake_start_time)
...
def test_two(self):
x = MyClass(start_time=self.fake_start_time)
...
All this works, but I'm wondering if there's a way for me to avoid having to write start_time=self.fake_start_time in every single test. Can I somehow replace the __init__ method's default argument in setUp()?
I figured out an ugly way to do it, but was wondering if there's a more standard approach, possibly based on mock.patch?

Create an instance of the class in setUp:
def setUp(self)
self.fake_start_time = time.time()
self.x = MyClass(start_time=self.fake_start_time)

functools.partial can be used to pre-bind arguments. So in this case, you can bind the time to the class constructor and then use the bound constructor without passing additional arguments:
from functools import partial
class MyclassTest(TestCase):
def setUp(self):
# Feel free to use a shorter name
self.FakeTimeMyClass = partial(MyClass, time.time())
def test_one(self):
x = self.FakeTimeMyClass()
...
def test_two(self):
x = self.FakeTimeMyClass()
...

Related

How to unit test a method called within __init__() function?

I am trying to test a class method which is called within an__init__ function.
class abc:
def __init__(path):
list = []
foo(path)
bar('hello') # test function bar
def foo(self, path):
# does a bunch of stuff and creates internal list
list =
def bar(self):
# does a bunch of stuff and uses list
I would like to write a test for method bar here which I guess must be called through an instance of class abc. I can mock list array for this test, but cannot understand how to avoid the call to foo().
Just mock foo method for the time of testing bar. You can use patch.object.
A full example below:
import unittest
from unittest.mock import patch
class MyClass:
def __init__(self, path):
self.list = []
self.foo(path)
self.bar('/init')
def foo(self, path):
self.list.append(path)
def bar(self, path):
self.list.insert(0, path)
class MyTestClass(unittest.TestCase):
#patch.object(MyClass, 'foo')
def test_bar_decorated(self, mock):
a = MyClass('/foo')
a.bar('/bar')
self.assertEqual(a.list, ['/bar', '/init']) # .foo() wasn't invoked
if __name__ == '__main__':
unittest.main()
Notice that, a mock is created for you and passed in as an extra argument to the decorated function (we don't use it in this test). To avoid that you can use context manager version of patch.object:
def test_bar_context_manager(self):
with patch.object(MyClass, 'foo'):
a = MyClass('/foo')
a.bar('/bar')
self.assertEqual(a.list, ['/bar', '/init']) # same behaviour

Adding existing function to subclass

I am making a few sub-classes that needs to implement a run method. They all follow a pattern:
from mylib import transformation_function_1
from mylib import transformation_function_2
def SubClass1(ParentClass):
def run(self):
subclass_data = transformation_function_1(self.parent_data)
# Some other fixed logic.
def SubClass2(ParentClass):
def run(self):
subclass_data = transformation_function_2(self.parent_data)
# Some other fixed logic.
Is there anyway I can pull out this logic in a intermediate class? Something like this?
from mylib import transformation_function_1
from mylib import transformation_function_2
def TransformationBase(ParentClass):
#abstractclassmethod
def transformation_function():
raise NotImplementedError
def run(self):
subclass_data = transformation_function(self.parent_data)
# Some other fixed logic.
def SubClass1(TransformationBase):
transformation_function = transformation_function_1
def SubClass2(TransformationBase):
transformation_function = transformation_function_2
Thanks!
The special function staticmethod allows to declare local methods as being static. Assuming that you wanted to declare classes and that you want that subclasses make use of external free functions, you could do:
>>> class TransformationBase:
def transformation_function():
raise NotImplementedError
def run(self):
subclass_data = self.transformation_function(self.parent_data)
# Some other fixed logic.
>>> def transformation_function_1(data):
print('F1', data)
>>> def transformation_function_2(data):
print('F2', data)
return 2
>>> class SubClass1(TransformationBase):
transformation_function = staticmethod(transformation_function_1)
parent_data = "P1"
>>> c1 = SubClass1()
>>> c1.run()
F1 P1
>>>
You can do something like:
class SubClass(TransformationBase):
def transformation_function(*args, **kwargs):
return transformation_function1(*args, **kwargs)

How to access a class method from a property definition

I have a model where I want to use a class method to set the default of for a property:
class Organisation(db.Model):
name=db.StringProperty()
code=db.StringProperty(default=generate_code())
#classmethod
def generate_code(cls):
import random
codeChars='ABCDEF0123456789'
while True: # Make sure code is unique
code=random.choice(codeChars)+random.choice(codeChars)+\
random.choice(codeChars)+random.choice(codeChars)
if not cls.all().filter('code = ',code).get(keys_only=True):
return code
But I get a NameError:
NameError: name 'generate_code' is not defined
How can I access generate_code()?
As I said in a comment, I would use a classmethod to act as a factory and always create you entity through there. It keeps things simpler and no nasty hooks to get the behaviour you want.
Here is a quick example.
class Organisation(db.Model):
name=db.StringProperty()
code=db.StringProperty()
#classmethod
def generate_code(cls):
import random
codeChars='ABCDEF0123456789'
while True: # Make sure code is unique
code=random.choice(codeChars)+random.choice(codeChars)+\
random.choice(codeChars)+random.choice(codeChars)
if not cls.all().filter('code = ',code).get(keys_only=True):
return code
#classmethod
def make_organisation(cls,*args,**kwargs):
new_org = cls(*args,**kwargs)
new_org.code = cls.generate_code()
return new_org
import random
class Test(object):
def __new__(cls):
cls.my_attr = cls.get_code()
return super(Test, cls).__new__(cls)
#classmethod
def get_code(cls):
return random.randrange(10)
t = Test()
print t.my_attr
You need specify the class name: Organisation.generate_code()

How do you mock patch a python class and get a new Mock object for each instantiation?

OK,
I know this is mentioned in the manual, and probably has to do with side_effect and/or return_value, but a simple, direct example will help me immensely.
I have:
class ClassToPatch():
def __init__(self, *args):
_do_some_init_stuff()
def some_func():
_do_stuff()
class UUT():
def __init__(self, *args)
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
Now, I want to unit test the UUT class, and mock the ClassToPatch. Knowing the UUT class will instantiate exactly two ClassToPatch objects, I want the Mock framework to return a new Mock object for each instantiation, so I can later assert calls on each separately.
How do I achieve this using the #patch decorator in a test case? Namely, how to fix the following code sample?
class TestCase1(unittest.TestCase):
#patch('classToPatch.ClassToPatch',autospec=True)
def test_1(self,mock1,mock2):
_assert_stuff()
Here's a quick'n'dirty example to get you going:
import mock
import unittest
class ClassToPatch():
def __init__(self, *args):
pass
def some_func(self):
return id(self)
class UUT():
def __init__(self, *args):
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
self.test_property = (resource_1.some_func(), resource_2.some_func())
class TestCase1(unittest.TestCase):
#mock.patch('__main__.ClassToPatch', autospec = True)
def test_1(self, mock1):
ctpMocks = [mock.Mock(), mock.Mock()]
ctpMocks[0].some_func.return_value = "funky"
ctpMocks[1].some_func.return_value = "monkey"
mock1.side_effect = ctpMocks
u = UUT()
self.assertEqual(u.test_property, ("funky", "monkey"))
if __name__ == '__main__':
unittest.main()
I've added test_property to UUT so that the unit test does something useful. Now, without the mock test_property should be a tuple containing the ids of the two ClassToPatch instances. But with the mock it should be the tuple: ("funky", "monkey").
I've used the side_effect property of the mock object so that a different instance of ClassToPatch is returned on each call in the UUT initialiser.
Hope this helps.
Edit: Oh, by the way, when I run the unit test I get:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Here is another version which is more generic to handle any number of instances created:
class TestUUT:
def test_init(self, mocker):
class MockedClassToPatchMeta(type):
static_instance = mocker.MagicMock(spec=ClassToPatch)
def __getattr__(cls, key):
return MockedClassToPatchMeta.static_instance.__getattr__(key)
class MockedClassToPatch(metaclass=MockedClassToPatchMeta):
original_cls = ClassToPatch
instances = []
def __new__(cls, *args, **kwargs):
MockedClassToPatch.instances.append(
mocker.MagicMock(spec=MockedClassToPatch.original_cls))
MockedClassToPatch.instances[-1].__class__ = MockedClassToPatch
return MockedClassToPatch.instances[-1]
mocker.patch(__name__ + '.ClassToPatch', new=MockedClassToPatch)
UUT()
# since your original code created two instances
assert 2 == len(MockedClassToPatch.instances)
If you need more thorough validation for each instance you can access MockedClassToPatch.instances[0] or MockedClassToPatch.instances[1].
I've also created a helper library to generate the meta class boilerplate for me. To generate the needed code for your example I wrote:
print(PytestMocker(mocked=ClassToPatch, name=__name__).mock_classes().mock_classes_static().generate())

Why this mocking script doesn't work (unittest, Mocker, python)

I want to mock MyClass.toBeMockedMethod which is called in MyClass.mymethod using the following script. It runs without actually mocking it. Not sure why....
class Test_mytest(MockerTestCase):
def mock_it_up(self, function, result=None, mmin=0, mmax=None):
function = self.m.mock()
function(ARGS)
self.m.result(result)
self.m.count(mmin, mmax)
def setUp(self):
self.m = Mocker()
self.mock_it_up(MyClass.toBeMockedMethod)
self.o=Myclass(0)
def test_one_atom(self):
self.o.mymethod()
def tearDown(self):
pass
if __name__ == '__main__':
main()
As with this question what you're really trying to do is patch your instance of MyClass. If MyClass is a new-style class then you can do this:
class Test_mytest(MockerTestCase):
def mock_it_up(self, function, result = None, mmin = 0, mmax = None):
methodToMock = getattr(self.p, function)
methodToMock()
self.m.result(result)
self.m.count(mmin, mmax)
def setUp(self):
self.m = Mocker()
self.o = MyClass(0)
self.p = self.m.patch(self.o)
self.mock_it_up('toBeMockedMethod')
# Put more calls to mock_it_up here.
self.m.replay()
def test_one_atom(self):
self.o.mymethod()
This will modify self.o so that calls to toBeMockedMethod are mocked.
However, if MyClass is not a new-style class then patching won't work. In this case, you can use type simulation to trick MyClass into doing what you want. For example:
class Test_mytest(MockerTestCase):
def mock_it_up(self, function, result = None, mmin = 0, mmax = None):
methodToMock = getattr(self.mockObj, function)
methodToMock()
self.m.result(result)
self.m.count(mmin, mmax)
def setUp(self):
self.m = Mocker()
self.o = MyClass(0)
self.mockObj = self.m.mock(MyClass)
self.mock_it_up('toBeMockedMethod')
# Put more calls to mock_it_up here.
self.m.replay()
def test_one_atom(self):
MyClass.mymethod(self.mockObj)
Note that the mocker's mock method is called with the class to be type-simulated. Later, instead of calling self.o.mymethod() we call MyClass.mymethod(...). Now MyClass.mymethod() expects an instance of MyClass as its first argument, but fortunately the mock object is masquerading as an instance of MyClass so the call goes through. When mymethod() calls toBeMockedMethod() it will actually call the mocked method, not the real method.
I quickly hacked up an test MyClass like this:
class MyClass():
def __init__(self, x):
self.x = x
def toBeMockedMethod(self):
print "Not Mocked!"
def mymethod(self):
self.toBeMockedMethod()
and when I ran this code as a unit test I got:
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
which is the desired result.

Categories