I am not sure why the following code is not working. I am using the Mock framework. Anyone could explain it to me?
The error I get is this:
$ python test_mock.py
Calls the mock object method. not a real one. ... ERROR
======================================================================
ERROR: Calls the mock object method. not a real one.
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_mock.py", line 37, in test_is_method_called
self.sut.do_something()
File "test_mock.py", line 21, in do_something
self.__obj.method()
File "build/bdist.linux-i686/egg/mock.py", line 365, in __getattr__
self._children[name] = self._get_child_mock(parent=self, name=name, wraps=wraps)
File "build/bdist.linux-i686/egg/mock.py", line 458, in _get_child_mock
return klass(**kw)
File "build/bdist.linux-i686/egg/mock.py", line 282, in __init__
self.reset_mock()
File "build/bdist.linux-i686/egg/mock.py", line 303, in reset_mock
self._return_value.reset_mock()
AttributeError: 'SentinelObject' object has no attribute 'reset_mock'
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
The code is:
import unittest
import mock
class Fubar ():
def __init__(self):
pass
def method (self):
print "I am a Stup!d Monkey!"
class Monkey ():
def __init__(self, obj):
self.__obj = obj
def do_something (self):
if not isinstance(self.__obj, Fubar):
raise RuntimeError
self.__obj.method()
class TestMoneky (unittest.TestCase):
def setUp(self):
self.mock_obj = mock.Mock(name="Mock object", spec=["method"])
self.sut = Monkey(self.mock_obj)
def tearDown(self):
pass
def test_is_method_called (self):
"""Calls the mock object method. not a real one."""
with mock.patch("__builtin__.isinstance") as mock_inst:
mock_inst.return_value = True
self.sut.do_something()
self.assertTrue(self.mock_obj.method.called)
def main ():
"""Simple runner."""
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestMoneky))
unittest.TextTestRunner(verbosity=2).run(suite)
if __name__ == '__main__':
main()
The problems (sadly) is trivial. I am patching isinstance() to always return True. So, somewhere in the bowels of the module, something is asking whether my mock object is a Sentinel and (since I am overriding the return value), True is returned. Thus the wrong internal behaviour is exhibited.
The solution is therefore to not patch isinstance() but instead provide the mock with a spec that matches the class it is supposed to match:
def setUp(self):
self.mock_obj = mock.Mock(name="Mock object", spec=Fubar)
self.sut = Monkey(self.mock_obj)
def test_is_method_called (self):
"""Calls the mock object method. not a real one."""
self.sut.do_something()
self.assertTrue(self.mock_obj.method.called)
Can anyone see a way to do this without coupling the mock to Fubar???...
Related
In a rather complex test scenario I need to mock the base class of one of my own classes and instantiate the latter several times. When I do that my test errors with a StopIteration exception. Here's what my scenario boils down to in this respect:
Code under test (my_class.py):
from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session
class MySession(OAuth2Session):
pass
class MyClass:
def init(self, x):
self.x = x
client = BackendApplicationClient(client_id=x)
self.session = MySession(client=client)
Test code (test_mock.py):
import unittest
from unittest.mock import patch
with patch('requests_oauthlib.OAuth2Session') as MockSession:
from my_class import MyClass
cls = MyClass()
class MockTest(unittest.TestCase):
def test_mock_1(self):
cls.init(1)
self.assertIsNotNone(cls.session)
def test_mock_2(self):
cls.init(2)
self.assertIsNotNone(cls.session)
Test result:
$ python -m unittest test_mock
.E
======================================================================
ERROR: test_mock_2 (test_mock.MockTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "...\test_mock.py", line 16, in test_mock_2
cls.init(2)
File "...\my_class.py", line 11, in init
self.session = MySession(client=client)
File "C:\Python39\lib\unittest\mock.py", line 1093, in __call__
return self._mock_call(*args, **kwargs)
File "C:\Python39\lib\unittest\mock.py", line 1097, in _mock_call
return self._execute_mock_call(*args, **kwargs)
File "C:\Python39\lib\unittest\mock.py", line 1154, in _execute_mock_call
result = next(effect)
StopIteration
----------------------------------------------------------------------
Ran 2 tests in 0.003s
FAILED (errors=1)
I have debugged into the unittest.mock.MagicMock class but I can't figure out what's going on. In MagicMock's _execute_mock_call() method I noticed that self.side_effect is a tuple iterator object and when next() is called on that in the second test (test_mock_2) it results in the StopIteration.
Both tests run "OK" if I don't use the MySession subclass, i.e. self.session = OAuth2Session(client=client) in MyClass' init() method. (But that's just not how the real code under test works...)
Any ideas anyone?
You should mock class which you directly use, because your custom class inherit Mock and next starts unexpected behavior.
Rewrite path method to your custom class.
import unittest
from unittest.mock import patch
with patch('my_class.MySession') as MockSession:
from my_class import MyClass
I'm trying to understand why a decorator on my class modifies the class in such way that it appears to be 'NoneType' when trying to import the class from another script.
In my_class.py I have:
my_dict = dict()
def register(cls):
name = cls.__name__
my_dict[name] = cls
#register # will be commented
class MyClass:
def my_method(self):
print("running class method")
print("my_dict: ", my_dict)
In another module my_main.py I import the class like
from my_class import MyClass
print(type(MyClass))
print(MyClass.my_method)
If I run it with $ python3 my_main.py I get the following output:
my_dict: {'MyClass': <class 'my_class.MyClass'>}
<class 'NoneType'>
Traceback (most recent call last):
File "my_main.py", line 4, in <module>
print(MyClass.my_method)
AttributeError: 'NoneType' object has no attribute 'my_method'
By commenting the #register line in my_class.py the my_main.py runs without error and outputs:
my_dict: {}
<class 'type'>
<function MyClass.my_method at 0x7ff06f254f28>
..but obviously my_dict is no longer filled. Is there a way of registering my_class with the given decorator AND accessing the attributes of the class after importing it in another script?
A decorator is no more than just a normal function.
I don't want to describe a lot about how to write a decorator rightly. But at least, you should let your decorator return a class.
That is similar when you write a decorator for a function. Decorator should at least return a function.
For example:
def decorator(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
print("Hi")
return method(*args, **kwargs)
return wrapper
When you use this decorator to decorate a function, you are actually pass that function to decorator: new_function = decorator(original_function).
Which means new_function is wrapped by wrapper. That is how decorator works. When you execute decorated function, it actually execute the wrapper:
print("Hi")
return method(*args, **kwargs) # pass all args to original function and return its return value.
In your code, your decorator just returns None.
This question already has answers here:
Deriving a class from TestCase throws two errors
(2 answers)
Closed 6 years ago.
I have this plain vanilla unit test, which works as expected, as long I leave out the constructor.
import sys
import unittest
class Instance_test(unittest.TestCase):
def __init__(self):
super(Instance_test, self).__init__()
self.attribute = "new"
def test_something(self):
pass
def test_other(self):
self.assertTrue(True)
pass
def setUp(self):
pass
def tearDown(self):
pass
def suite():
return unittest.makeSuite(Instance_test, "test")
def main():
runner = unittest.TextTestRunner(sys.stdout)
runner.run(suite())
if __name__ == "__main__":
main()
With the constructor in place in get this backtrace:
Traceback (most recent call last):
File "f:\gt\check.py", line 31, in main()
File "f:\gt\check.py", line 28, in main
runner.run(suite())
File "f:\gt\check.py", line 24, in suite
return unittest.makeSuite(Instance_test, "test")
File "C:\Python34\lib\unittest\loader.py", line 374, in makeSuite
testCaseClass)
File "C:\Python34\lib\unittest\loader.py", line 70, in loadTestsFromTestCase
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
File "C:\Python34\lib\unittest\suite.py", line 24, in __init__
self.addTests(tests)
File "C:\Python34\lib\unittest\suite.py", line 60, in addTests
for test in tests:
TypeError: __init__() takes 1 positional argument but 2 were given
What's wrong and how else could I have a central attribute to be shared by different test_xxx methods?
I would use unittest.TestCase's setUp() and tearDown() methods instead of init. Just do the same thing you're doing except use the setUp method.
It is happening because __init__ takes more than one arguments, you forgot to provide args and kwargs as arguments. It should have been like this -
class Instance_test(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(Instance_test, self).__init__(*args, **kwargs)
self.attribute = "new"
Besides why are you overriding __init__ anyway (as 2achary suggested) when setUp method is exactly meant for this purpose.
class Instance_test(unittest.TestCase):
def setUp(self):
self.attribute = 'new'
Just add an argument named methodName to your own __init__ method and you should be good to go:
class Instance_test(unittest.TestCase):
def __init__(self, methodName='runTest'):
super(Instance_test, self).__init__(methodName=methodName)
self.attribute = "new"
...
The base TestCase.__init__ signature is as follows:
class TestCase(object):
def __init__(self, methodName='runTest'):
...
As you can see, it takes an additional methodName argument, and there is no constructor without it, hence the failure you encountered.
I'm quite a beginner in Python and started designing a unit test in Python and i need to post some messages to the server before i run the test class (cause it's gonna search for them). Thus i need to call a non-static method postMessages().
the stack-trace of the error i'm getting is this-
Error
Traceback (most recent call last):
File ".../TestMsgs.py", line 23, in setUpClass
instance = cls()
File ".../python2.7/unittest/case.py", line 191, in __init__
(self.__class__, methodName))
ValueError: no such test method in <class 'TestMsgs.TestMsgs'>: runTest
i have something like this in the code:
class A(object):
def postMessages(self):
print "i post messages in the server!"
class B(A):
#classmethod
def setUpClass(cls):
cls.foo() # should post messages for the tests in the class to work on
There's no option, right now, to make foo static. How can i instantiate B (or A, for that matter) in postMessages() so i can use it in setUpClass() ?
After having a read through the __init__ method for TestCase I see that you need to provide a test method name to it. The default is "runTest" which is why that error was popping up.
import unittest
class A(unittest.TestCase):
def postMessages(self):
print "i post messages in the server!"
class B(A):
#classmethod
def setUpClass(cls):
cls.foo(cls(methodName='test_method')) # should post messages for the tests in the class to work on
def foo(self):
self.postMessages()
def test_method(self):
pass
B.setUpClass()
You can see it running in an interactive Python console here. It will print out "i post messages in the server!"
The reason you need to pass in a valid method name in the class can be clearly seen in the source code for unittest:
class TestCase:
"""A class whose instances are single test cases."""
def __init__(self, methodName='runTest'):
"""Create an instance of the class that will use the named test
method when executed. Raises a ValueError if the instance does
not have a method with the specified name.
"""
try:
self._testMethodName = methodName
testMethod = getattr(self, methodName)
self._testMethodDoc = testMethod.__doc__
except AttributeError:
raise ValueError, "no such test method in %s: %s" % \
(self.__class__, methodName)
If you want to pass in parameters to the method that you have just passed in then you would need to do something like
class A(unittest.TestCase):
def foo(self, arg1):
pass
a = A(methodName='foo')
a.foo('an_argument')
But this whole question just feels really wrong. You should refactor rather than have a static method calling an instance method. It's just silly.
I am attempting to write a unit test for a class's __init__:
def __init__(self, buildNum, configFile = "configfile.txt"):
super(DevBuild, self).__init__(buildNum, configFile)
if configFile == "configfile.txt":
self.config.MakeDevBuild()
The config attribute is set by the super's __init__. I'm using mock, and I want the config attribute to be a mock object. However, I haven't been able to figure out how to actually make that happen. Here's the best I could come up with for the test:
def test_init(self):
with patch('DevBuild.super', create=True) as mock_super:
mock_MakeDevBuild = MagicMock()
mock_super.return_value.config.MakeDevBuild = mock_MakeDevBuild
# Test with manual configuration
self.testBuild = DevBuild("42", "devconfigfile.txt")
self.assertFalse(mock_MakeDevBuild.called)
# Test with automated configuration
self.testBuild = DevBuild("42")
mock_MakeDevBuild.assert_called_once_with()
However, this doesn't work--I get an error:
Error
Traceback (most recent call last):
File "/Users/khagler/Projects/BuildClass/BuildClass/test_devBuild.py", line 17, in test_init
self.testBuild = DevBuild("42")
File "/Users/khagler/Projects/BuildClass/BuildClass/DevBuild.py", line 39, in __init__
self.config.MakeDevBuild()
AttributeError: 'DevBuild' object has no attribute 'config'
Clearly I'm not setting the config attribute correctly, but I have no idea where exactly I should be setting it. Or for that matter, if what I want to do is even possible. Can anyone tell me what I need to do to make this work?
You can't mock __init__ by setting it directly - see _unsupported_magics in mock.py.
As for what you can do, you can mock __init__ by passing it to patch, like so:
mock_makeDevBuild = MagicMock()
def mock_init(self, buildNum, configFile):
self.config = MagicMock()
self.config.MakeDevBuild = mock_makeDevBuild
with patch('DevBuild.SuperDevBuild.__init__', new=mock_init):
DevBuild("42")
mock_makeDevBuild.assert_called_once_with()
where SuperDevBuild is a base class of DevBuild.
If you really want to mock super(), you can perhaps make a class and then bind __init__ to object manually, like
mock_makeDevBuild = MagicMock()
def get_mock_super(tp, obj):
class mock_super(object):
#staticmethod
def __init__(buildNum, configFile):
obj.config = MagicMock()
obj.config.MakeDevBuild = mock_makeDevBuild
return mock_super
with patch('DevBuild.super', create=True, new=get_mock_super):
DevBuild("42")
mock_makeDevBuild.assert_called_once_with()
which works, but is quite ugly..
I do it this way, mocking the inherited class init:
from unittest import mock
#mock.patch.object(HierarchicalConf, "__init__")
def test_super_init(self, mock_super_init):
# act
ConfigurationService('my_args')
# assert
mock_super_init.assert_called_once_with(args)
Given the class:
class ConfigurationService(HierarchicalConf):
def __init__(self, dag_name) -> None:
"""Wrapper of Hierarchical Conf."""
# ... my code
super().__init__(args)
And if you want to also mock the ConfigurationService init, you can do quite the same:
#mock.patch.object(ConfigurationService, "__init__")
def test_init(self, mock_init):
# act
ConfigurationService('my_args')
# assert
mock_init.assert_called_once_with('my_args')