I have a Python module as follows:
# src/exec.py
class A:
def run(self, stuff):
b = B(stuff.x)
class B:
def __init__(self, x):
self.obj = self.create_some_obj()
I'm trying to test a part of class A independently, for which I need to replace the obj in B with a fake object. I'm doing this as follows:
# test/test_execs.py
import exec as ex
class FakeObjForB:
def __init__(self):
# some init
class TestClass:
#patch.object(ex.B, 'obj', FakeObjForB())
def test_with_fake_obj(self):
a = ex.A()
a.run()
# assert something about the state of a that depends on the b inside its run method
Running this test gives me the error: AttributeError: <class 'B'> does not have the attribute 'obj'. I tried replacing the line with the #patch decorator with #patch.object(ex.B, 'obj', FakeObjForB(), create=True). This, however, results in b.obj using the actual definition, and not FakeObjForB, which in turn leads to a false-failure in the assertion in test_with_fake_obj. Any clues about what I'm doing incorrectly here?
In your example you're patching the B class, that's the object passed as the first argument. That class doesn't declare obj attribute on the class level and so AttributeError is raised. When you provide create=True it won't complain as that argument allows the obj attribute to be dynamically created when needed/accessed. But, that won't ever happen as the very first "access" of that attribute is its actual creation - no dynamic mocking ever happened.
A solution is to actually patch the method whose returned value would be assigned to the obj attribute, like:
#patch.object(ex.B, 'create_some_obj', FakeObjForB())
Related
I would like to assert from a UT, TestRunner.test_run that some deeply nested function Prompt.run_cmd is called with the string argument "unique cmd". My setup besically resembles the following:
# Module application/engine/prompt.py
class Prompt:
def run_cmd(self, input):
pass
# Module: application/scheduler/runner.py
class Runner:
def __init__(self):
self.prompt = application.engine.prompt.Prompt()
def run(self):
self.prompt.run_cmd("unique cmd")
# Module tests/application/scheduler/runner_test.py
class TestRunner(unittest.TestCase):
...
def test_run(self):
# calls Runner.run
# Objective assert that Prompt.run is called with the argument "unique cmd"
# Current attempt below:
with mock.patch(application.engine.prompt, "run_cmd") as mock_run_cmd:
pass
Unfortunately my attempts to mock the Prompt.run_cmd fail with the error message
AttributeError: 'module' object has no attribute 'object'
If you wanted to patch a concrete instance, you could easily do this using mock.patch.object and wraps (see for example this question.
If you want to patch your function for all instances instead, you indeed have to use mock.patch. In this case you could only mock the class itself, as mocking the method would not work (because it is used on instances, not classes), so you cannot use wraps here (at least I don't know a way to do this).
What you could do instead is derive your own class from Prompt and overwrite the method to collect the calls yourself. You could then patch Prompt by your own implementation. Here is a simple example:
class MyPrompt(Prompt):
calls = []
def run_cmd(self, input):
super().run_cmd(input)
# we just add a string in the call list - this could be more sophisticated
self.__class__.calls.append(input)
class TestRunner(unittest.TestCase):
def test_run(self):
with mock.patch("application.engine.prompt.Prompt", MyPrompt) as mock_prompt:
runner = Runner()
runner.run()
self.assertEqual(["unique cmd"], mock_prompt.calls)
I'm using python 3.9.2 with unittest and mock to patch out a class.
My code under test instantiates an object of the class and mock returns a MagicMock object as the instance.
My question is, can I access that object from my test code?
I can see the call that instantiates the class in the mock_calls list, but cannot find a way of accessing the instance that is returned from that call.
The reason I need to access the instance is that my code under test attaches attributes to the instance rather than call methods on it. It is easy to test method calls, but is there a direct way to test attributes?
Upon investigation I found that there was only a single instance of a MagicMock being created and returned each time I instantiated my class. This behaviour was not convenient for me due to the attributes that I add to the class.
I created the following test aid to support my needs. This is not general-purpose but could be adapted for other circumstances.
class MockMyClass():
"""mock multiple MyClass instances
Note - the code under test must add a name attribute to each instance
"""
def __init__(self):
self.myclass = []
def factory(self, /, *args, **kwargs):
"""return a new instance each time called"""
new = mock.MagicMock()
# override __enter__ to enable the with... context manager behaviour
# for convenience in testing
new.__enter__ = lambda x: new
self.myclass.append(new)
return new
def __getitem__(self, key: str) -> None:
"""emulate a dict by returning the named instance
use as
mockmyclass['name'].assert_called_once()
or
with mockmyclass['name'] as inst:
inst.start().assert_called_once()
"""
# Important - the code under test gives the instance a name
# attribute and this relies on that attribute so is not
# general purpose
wanted = [t for t in self.myclass if t.name == key]
if not wanted:
names = [t.name for t in self.myclass]
raise ValueError(f'no timer {key} in {names}')
return wanted[0]
class TestBehaviour(unittest.TestCase):
def setUp(self):
self.mockmyclass = MockMyClass()
self.mocked = mock.patch(
'path-to-my-file.MyClass',
side_effect=self.mockmyclass.factory,
)
self.addCleanup(self.mocked.stop)
self.mocked = self.mocked.start()
def test_something(self):
# call code under test
# then test with
with self.mockmyclass['name-of-instance'] as inst:
inst.start.assert_called_once()
inst.stop.assert_called_once()
# or test with
self.mockmyclass['name-of-instance'].start.assert_called_once()
self.assertFalse(b.__is_manual) AttributeError: 'BaseResource' object has no attribute '_Resources__is_manual'
My test_resources.py is
class Resources(TestCase):
def test_disable_manual_mode(self):
self.assertFalse(b.__is_manual)
if __name__=='__main__':
b = base.BaseResource()
unittest.main()
And My base.py is
class BaseResource(object):
def __init__(self, index=0, parent=None, **kwargs):
self.__is_manual = False
def disable_manual_mode(self):
self.__is_manual = False
Both are in same directory I want to import __is_manual in test_resouces.py
How do i do it.
I have tried b.__is_manual but it is giving error(mentioned above)
According to Python docs
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.
The instantiation of object must be inside the test class.
When naming the attribute to __is_manual, you are defining it as a "protected" attribute, and you can not access it. Simplify your code.
class BaseResource(object):
def __init__(self, index=0, parent=None, **kwargs):
self.is_manual = False
def disable_manual_mode(self):
self.is_manual = False
Also, the instantiation of object must be inside the test class.
class Resources(TestCase):
def test_disable_manual_mode(self):
b = base.BaseResource()
self.assertFalse(b.is_manual)
if __name__=='__main__':
unittest.main()
We can't access __is_manual. Because we can't access a variable starting with __ (double underscore).
How do I call a function defined in a class in Python?
import pypyodbc as pyodbc
Master_Conn = 'Driver={SQL Server};Server=server\23;Database=DBname;UID=username;PWD=password;'
Master_db = pyodbc.connect(Master_Conn)
Master_Cursor = Master_db.cursor()
class Test:
def __init__(self):
self.master_status = ""
def Getmodel(self):
self.master_status= dict(Master_Cursor.execute("select col1,col2 from tablename ").fetchall())
print (self.master_status)
Test.Getmodel()
With above code, I get
TypeError: Getmodel() missing 1 required positional argument: 'self'
So I tried Test.Getmodel(self) and it resulted in
NameError: name 'self' is not defined.
I even tried both scenarios with if __name__== '__main__': but got same errors.
You are defining Getmodel as an instance method. So it have to be called on an instance of the class Test.
To create an instance of class Test you can write
instance_of_test = Test()
Now you can call Getmodel on instance_of_test
instance_of_test.Getmodel()
You can shorten this process by writing
Test().Getmodel()
Note that self is (usually) passed hiddenly when calling an instance method; it represents the instance calling the method.
Consider the following class Pizza
class Pizza:
def __init__(self, size):
self.size = size
def get_size(self):
return self.size
First you need to create an instance of Pizza
mypizza = Pizza(42)
then you can call the instance method get_size on mypizza
mypizza.get_size()
What your doing will work if you instantiate an object of the class Test first. Instantiation just means you call the class as if it were a method which. The following should work:
Test().Getmodel()
The () is what makes the instantiation happen so now the self essentially gets passed to the Getmodel() method because an object of the class Test now exists.
Suppose I have this snippet inside a module
def func(params):
class MyClass(object):
pass
How can I pickle an instance of the class MyClass ?
You can't, because picklable object's class definitions must reside in an imported module's scope. Just put your class inside module scope and you are good to go.
That said, in Python there is very little that can't be achieved with a bit of hacking the insides of the machinery (sys.modules in this case), but I wouldn't recommend that.
The MyClass definition is local variable for the func function. You cannot directly create an instance of it, but you can map it's functions to a new class, and then to use the new class as it is the original one. Here's an example:
def func(params):
class MyClass(object):
some_param = 100
def __init__(self, *args):
print "args:", args
def blabla(self):
self.x = 123
print self.some_param
def getme(self):
print self.x
func.func_code is the code of the func function, and func.func_code.co_consts[2] contains the bytecode of the MyClass definition:
In : func.func_code.co_consts
Out:
(None,
'MyClass',
<code object MyClass at 0x164dcb0, file "<ipython-input-35-f53bebe124be>", line 2>)
So we need the bytecode for the MyClass functions:
In : eval(func.func_code.co_consts[2])
Out:
{'blabla': <function blabla at 0x24689b0>,
'__module__': '__main__',
'getme': <function getme at 0x2468938>,
'some_param': 100,
'__init__': <function __init__ at 0x219e398>}
And finally we create a new class with metaclass, that assigns the MyClass functions to the new class:
def map_functions(name, bases, dict):
dict.update(eval(func.func_code.co_consts[2]))
return type(name, bases, dict)
class NewMyClass(object):
__metaclass__ = map_functions
n = NewMyClass(1, 2, 3, 4, 5)
>> args: (1, 2, 3, 4, 5)
n.blabla()
>> 100
n.getme()
>> 123
This is somewhat tough to do because the way Pickle does with objects from user defined classes by default is to create a new instance of the class - using the object's __class__.__name__ attribute to retrieve its type in the object's original module. Which means: pickling and unpickling only works (by default) for classes that have well defined names in the module they are defined.
When one defines a class inside a function, usulay there won't be a module level (i.e. global) variable holding the name of each class that was created inside the function.
The behavior for pickle and npickle can be customized through the __getstate__ and __setstate__ methods on the class - check the docs - but even them, doing it right for dynamic class can be tricky , but I managed to create a working implementation of it for another S.O. question - -check my answer here:
Pickle a dynamically parameterized sub-class
You can work around the pickle requirement that class definitions be importable by including the class definition as a string in the data pickled for the instance and exec()uting it yourself when unpickling by adding a __reduce__() method that passes the class definition to a callable. Here's a trivial example illustrating what I mean:
from textwrap import dedent
# Scaffolding
definition = dedent('''
class MyClass(object):
def __init__(self, attribute):
self.attribute = attribute
def __repr__(self):
return '{}({!r})'.format(self.__class__.__name__, self.attribute)
def __reduce__(self):
return instantiator, (definition, self.attribute)
''')
def instantiator(class_def, init_arg):
""" Create class and return an instance of it. """
exec(class_def)
TheClass = locals()['MyClass']
return TheClass(init_arg)
# Sample usage
import pickle
from io import BytesIO
stream = BytesIO() # use a memory-backed file for testing
obj = instantiator(definition, 'Foo') # create instance of class from definition
print('obj: {}'.format(obj))
pickle.dump(obj, stream)
stream.seek(0) # rewind
obj2 = pickle.load(stream)
print('obj2: {}'.format(obj2))
Output:
obj: MyClass('Foo')
obj2: MyClass('Foo')
Obviously it's inefficient to include the class definition string with every class instance pickled, so that redundancy may make it impractical, depending on the the number of class instances involved.