I've been chasing an issue with implementing mock for testing a Pylons controller function.
The particular function I am trying to test can be found here: https://hg.mozilla.org/build/buildapi/file/efe11511f42d/buildapi/controllers/selfserve.py#l210
The Objective:
I am simply trying to mock out the functions that it calls so that I can confirm that it eventually calls get_completeness.
The Issue:
I can mock _ok, _get_stable_delay, _failed, and _branches_cache just fine. But when it comes to g.buildapi_cache.get_builds_for_revision, I cannot seem to mock it correctly, and each time I call revision(), it fails.
g is actually pylons.app_globals and is imported in the selfserve.py controller like so: 'from pylons import app_globals as g'
Testing:
I am using a python interpreter to test all of this. These are the 2 implementations I am trying: http://pastebin.mozilla.org/2931825 and http://pastebin.mozilla.org/2931832
All in all here is the complete run in the interpreter, complete with Traceback for http://pastebin.mozilla.org/2931825
>>> from buildapi.controllers.selfserve import SelfserveController
>>> from mock import patch, Mock
>>> s = SelfserveController()
>>> s._ok = Mock(name='_ok')
>>> s._get_stable_delay = Mock(return_value=180)
>>> s._branches_cache = Mock()
>>> s._branches_cache = {'try': None}
>>> s._failed = Mock(return_value="Failed")
>>> with patch('buildapi.controllers.selfserve.g.buildapi_cache.get_builds_for_revision') as get_builds_for_revision:
... get_builds_for_revision.return_value = ['baz', 'bang']
... result = revision('try', 'bar')
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/mock.py", line 1252, in __enter__
self.target = self.getter()
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/mock.py", line 1414, in <lambda>
getter = lambda: _importer(target)
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/mock.py", line 1102, in _importer
thing = _dot_lookup(thing, comp, import_path)
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/mock.py", line 1089, in _dot_lookup
return getattr(thing, comp)
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/paste/registry.py", line 137, in __getattr__
return getattr(self._current_obj(), attr)
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/paste/registry.py", line 197, in _current_obj
'thread' % self.____name__)
TypeError: No object (name: app_globals) has been registered for this thread
And here it is for http://pastebin.mozilla.org/2931832
>>> from buildapi.controllers.selfserve import SelfserveController
>>> from mock import patch, Mock
>>> s = SelfserveController()
>>> s._ok = Mock(name='_ok')
>>> s._get_stable_delay = Mock(return_value=180)
>>> s._branches_cache = Mock()
>>> s._branches_cache = {'try': None}
>>> s._failed = Mock(return_value="Failed")
>>> g = Mock()
>>> g.buildapi_cache = Mock(name='buildapi_cache')
>>> g.buildapi_cache.get_builds_for_revision = Mock(name='get_builds_for_revision', return_value=['baz', 'bang'])
>>> s.revision('try', 'bar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "buildapi/controllers/selfserve.py", line 217, in revision
job_items = g.buildapi_cache.get_builds_for_revision(branch, revision)
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/paste/registry.py", line 137, in __getattr__
return getattr(self._current_obj(), attr)
File "/Users/jzeller/buildapi-test/lib/python2.7/site-packages/paste/registry.py", line 197, in _current_obj
'thread' % self.____name__)
TypeError: No object (name: app_globals) has been registered for this thread
This is the same error that I have been getting when I try to run revision. Each and every time it errors out here because my attempt to mock this app_globals module has failed, or so it seems. I am really not sure how else to try this. Does anyone have any ideas?
Here are a few more good details:
- Here is the function I am attempting to use, which is in the class I am instantiating: https://hg.mozilla.org/build/buildapi/file/efe11511f42d/buildapi/controllers/selfserve.py#l210
Note 1
I have already looked at the following threads which do not seem to quite apply to this specific scenario, or I am completely missing the point.
A controller could not be used as an independent object without an initialized Pylons' environment.
Using the functionality Pylons provides for testing saves you from all the boilerplate code to test a Controller.
I recommend you to use the TestController class in combination with nosetests as shown in documentation.
Specifically for your code, I think Pylons treats private methods (starting with _) differently from public methods because public methods run in the context of a request.