mocker-1.0/0000755000175000017500000000000011407524705013053 5ustar niemeyerniemeyermocker-1.0/mocker.egg-info/0000755000175000017500000000000011407524705016025 5ustar niemeyerniemeyermocker-1.0/mocker.egg-info/PKG-INFO0000644000175000017500000000123411407524705017122 0ustar niemeyerniemeyerMetadata-Version: 1.0 Name: mocker Version: 1.0 Summary: Graceful platform for test doubles in Python (mocks, stubs, fakes, and dummies). Home-page: http://labix.org/mocker Author: Gustavo Niemeyer Author-email: gustavo@niemeyer.net License: BSD Download-URL: https://launchpad.net/mocker/+download Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Testing Classifier: Topic :: Software Development :: Libraries :: Python Modules mocker-1.0/mocker.egg-info/SOURCES.txt0000644000175000017500000000026511407524705017714 0ustar niemeyerniemeyerLICENSE MANIFEST.in NEWS mocker.py setup.cfg setup.py test.py mocker.egg-info/PKG-INFO mocker.egg-info/SOURCES.txt mocker.egg-info/dependency_links.txt mocker.egg-info/top_level.txtmocker-1.0/mocker.egg-info/dependency_links.txt0000644000175000017500000000000111407524705022073 0ustar niemeyerniemeyer mocker-1.0/mocker.egg-info/top_level.txt0000644000175000017500000000000711407524705020554 0ustar niemeyerniemeyermocker mocker-1.0/LICENSE0000644000175000017500000000302111407425542014053 0ustar niemeyerniemeyerCopyright (c) 2007-2010, Gustavo Niemeyer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mocker-1.0/MANIFEST.in0000644000175000017500000000010611032441006014570 0ustar niemeyerniemeyerinclude mocker.py test.py setup.py setup.cfg MANIFEST.in LICENSE NEWS mocker-1.0/NEWS0000644000175000017500000001274111407523624013556 0ustar niemeyerniemeyer1.0 (2010-06-20) ================= - Changed license to BSD, since the PSF license only applies to Python itself (#583335). - Unwrap bound methods on replace() and proxy(), as suggested by James Henstridge (#270782). - MockerTestCase.assertRaises() will now return the exception raised, allowing further inspection of the raised exception (implemented by Thomas Hervé) (#299930). - Fixed support for Python 2.6. Mocking of iterators was broken in certain cases because, even though that's *not* documented, Python tries to use __length_hint__ in some cases. - Fixed support for MockerTestCase.addCleanup() in Python 2.3, by Anders F Björklund (#528657). - Implemented Expect helper, which allows creating a new expect() "function" with an explicitly provided Mocker instance. This helps in cases where the expression can't result in a Mock instance (e.g. expect(iter(mock))) (#196388, #179072). - __nonzero__ should necessarily return a boolean value, so transform Mock results into True (#380024). - Applied change suggested by David Glick to avoid reimporting modules (#529675). - When setting the temporary __mocker_mock__ attribute, use Mocker.patch() so that by the end of the mocking it's properly removed (by Thomas Herve). - Prevent the MockerTestCase base from leaving the mocker in replay mode while the base class run() method runs, since this might have additional logic which touches mocked content (time.time() was one case). Thanks to Thomas Herve for the initial debugging. - Ensure that the raised AttributeError exception on a patched object exposes the real problem rather than a mocker error (by Duncan McGreggor). - When cleaning up on MockerTestCase, use reset() rather than restore(), so that the same test case instance may be run more than once (like Trial does). - Some tweaks to prepare for Python 3. - Added MockerTestCase to __all__. 0.10.1 (2007-12-11) =================== - Fixed patching of objects which define __getattr__. 0.10 (2007-12-09) ================= - Greatly improved error messages and logic for expression ordering! - Implemented MockerTestCase.addCleanup(). It allows one to register cleanup functions to be called after the test is complete. - MockerTestCase now verifies if the mocker is put in replay mode in cases where events were recorded. - New MATCH() argument matcher, which allows using a function to match an argument generically. E.g. MATCH(lambda x: x > 10) - New 'path' option to MockerTestCase.makeFile() and makeDir(), which allows setting the full target path with a single option. - Now when a spec is provided (or with proxy/replace/patch) the existence of the real method is checked even if the mocked method doesn't execute. This is useful to detect API expectation errors even if count(0) is used (a negative assertion). - Implemented in MockerTestCase support for Deferred results as understood by Twisted Trial's TestCase, so that coexistence by multiple inheritance is possible and trivial. - MockerTestCase.makeFile() with content=None (the default) now consistently returns an unexistent temporary filename which is properly cleaned up if created. - Fixed problem when requesting order on similar expressions. The second expression might not be accepted. - When the expression executed isn't exactly the same as the recorded events (e.g. when parameter matchers are used), show in the error message the real expression run, to aid in debugging. 0.9.3 (2007-11-24) ================== - Added support for Python 2.3 (patch by Phillip J. Eby). - Added MockerTestCase.assert{True,False} aliases, so that they're available even in Python 2.3. - Introduced automatic test coverage verification as part of the test suite, based on the 'coverage' module by Ned Batchelder, to ensure that it continues to have 100% of statement coverage. 0.9.2 (2007-11-22) ================== - In recording mode, mock.__class__ will now return Mock, and not record the action. This allows Mocker to be used in interactive environments which inspect the result's type, such as in iPython (reported by Alex Dante). - Now Mocker.mock()/proxy()/replace() accept a 'count' keyword parameter, which if set to False, the default behavior of allowing expressions just once is disabled. 0.9.1 (2007-11-18) ================== - Fixed setup.py to install mocker.py properly. 0.9 (2007-11-17) ================ - Added MockerTestCase.makeFile() and .makeDir() helpers. They offer easy creation of temporary files/directories, and ensure that they get removed after each test method runs. - Added MockerTestCase.assertMethodsMatch(). It will verify if all public methods found in the class passed as the first argument are also present in the class passed as the second argument, and that they accept the same arguments. This is useful to verify if a fake or stub class has the same API as the real class being simulated. - Added MockerTestCase.assert[Not]{Starts,Ends}With(). - If the replay() method is called twice, expectations will be fully reset so that several similar tests may be performed in a row by just calling replay() again. - Mocker.on_restore() removed. Restore isn't performed if replay() isn't called, and that may not be obvious, so a hook won't be exposed for now. - When using a non-existent import path for Mocker.proxy(), raise an ImportError on the base module, rather than using the actual string as the object (#162315). 0.8 (2007-11-11) ================ Released! mocker-1.0/mocker.py0000644000175000017500000023164311407520136014710 0ustar niemeyerniemeyer""" Mocker Graceful platform for test doubles in Python: mocks, stubs, fakes, and dummies. Copyright (c) 2007-2010, Gustavo Niemeyer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import __builtin__ import tempfile import unittest import inspect import shutil import types import sys import os import gc if sys.version_info < (2, 4): from sets import Set as set # pragma: nocover __all__ = ["Mocker", "Expect", "expect", "IS", "CONTAINS", "IN", "MATCH", "ANY", "ARGS", "KWARGS", "MockerTestCase"] __author__ = "Gustavo Niemeyer " __license__ = "BSD" __version__ = "1.0" ERROR_PREFIX = "[Mocker] " # -------------------------------------------------------------------- # Exceptions class MatchError(AssertionError): """Raised when an unknown expression is seen in playback mode.""" # -------------------------------------------------------------------- # Helper for chained-style calling. class expect(object): """This is a simple helper that allows a different call-style. With this class one can comfortably do chaining of calls to the mocker object responsible by the object being handled. For instance:: expect(obj.attr).result(3).count(1, 2) Is the same as:: obj.attr mocker.result(3) mocker.count(1, 2) """ __mocker__ = None def __init__(self, mock, attr=None): self._mock = mock self._attr = attr def __getattr__(self, attr): return self.__class__(self._mock, attr) def __call__(self, *args, **kwargs): mocker = self.__mocker__ if not mocker: mocker = self._mock.__mocker__ getattr(mocker, self._attr)(*args, **kwargs) return self def Expect(mocker): """Create an expect() "function" using the given Mocker instance. This helper allows defining an expect() "function" which works even in trickier cases such as: expect = Expect(mymocker) expect(iter(mock)).generate([1, 2, 3]) """ return type("Expect", (expect,), {"__mocker__": mocker}) # -------------------------------------------------------------------- # Extensions to Python's unittest. class MockerTestCase(unittest.TestCase): """unittest.TestCase subclass with Mocker support. @ivar mocker: The mocker instance. This is a convenience only. Mocker may easily be used with the standard C{unittest.TestCase} class if wanted. Test methods have a Mocker instance available on C{self.mocker}. At the end of each test method, expectations of the mocker will be verified, and any requested changes made to the environment will be restored. In addition to the integration with Mocker, this class provides a few additional helper methods. """ def __init__(self, methodName="runTest"): # So here is the trick: we take the real test method, wrap it on # a function that do the job we have to do, and insert it in the # *instance* dictionary, so that getattr() will return our # replacement rather than the class method. test_method = getattr(self, methodName, None) if test_method is not None: def test_method_wrapper(): try: result = test_method() except: raise else: if (self.mocker.is_recording() and self.mocker.get_events()): raise RuntimeError("Mocker must be put in replay " "mode with self.mocker.replay()") if (hasattr(result, "addCallback") and hasattr(result, "addErrback")): def verify(result): self.mocker.verify() return result result.addCallback(verify) else: self.mocker.verify() self.mocker.restore() return result # Copy all attributes from the original method.. for attr in dir(test_method): # .. unless they're present in our wrapper already. if not hasattr(test_method_wrapper, attr) or attr == "__doc__": setattr(test_method_wrapper, attr, getattr(test_method, attr)) setattr(self, methodName, test_method_wrapper) # We could overload run() normally, but other well-known testing # frameworks do it as well, and some of them won't call the super, # which might mean that cleanup wouldn't happen. With that in mind, # we make integration easier by using the following trick. run_method = self.run def run_wrapper(*args, **kwargs): try: return run_method(*args, **kwargs) finally: self.__cleanup() self.run = run_wrapper self.mocker = Mocker() self.expect = Expect(self.mocker) self.__cleanup_funcs = [] self.__cleanup_paths = [] super(MockerTestCase, self).__init__(methodName) def __call__(self, *args, **kwargs): # This is necessary for Python 2.3 only, because it didn't use run(), # which is supported above. try: super(MockerTestCase, self).__call__(*args, **kwargs) finally: if sys.version_info < (2, 4): self.__cleanup() def __cleanup(self): for path in self.__cleanup_paths: if os.path.isfile(path): os.unlink(path) elif os.path.isdir(path): shutil.rmtree(path) self.mocker.reset() for func, args, kwargs in self.__cleanup_funcs: func(*args, **kwargs) def addCleanup(self, func, *args, **kwargs): self.__cleanup_funcs.append((func, args, kwargs)) def makeFile(self, content=None, suffix="", prefix="tmp", basename=None, dirname=None, path=None): """Create a temporary file and return the path to it. @param content: Initial content for the file. @param suffix: Suffix to be given to the file's basename. @param prefix: Prefix to be given to the file's basename. @param basename: Full basename for the file. @param dirname: Put file inside this directory. The file is removed after the test runs. """ if path is not None: self.__cleanup_paths.append(path) elif basename is not None: if dirname is None: dirname = tempfile.mkdtemp() self.__cleanup_paths.append(dirname) path = os.path.join(dirname, basename) else: fd, path = tempfile.mkstemp(suffix, prefix, dirname) self.__cleanup_paths.append(path) os.close(fd) if content is None: os.unlink(path) if content is not None: file = open(path, "w") file.write(content) file.close() return path def makeDir(self, suffix="", prefix="tmp", dirname=None, path=None): """Create a temporary directory and return the path to it. @param suffix: Suffix to be given to the file's basename. @param prefix: Prefix to be given to the file's basename. @param dirname: Put directory inside this parent directory. The directory is removed after the test runs. """ if path is not None: os.makedirs(path) else: path = tempfile.mkdtemp(suffix, prefix, dirname) self.__cleanup_paths.append(path) return path def failUnlessIs(self, first, second, msg=None): """Assert that C{first} is the same object as C{second}.""" if first is not second: raise self.failureException(msg or "%r is not %r" % (first, second)) def failIfIs(self, first, second, msg=None): """Assert that C{first} is not the same object as C{second}.""" if first is second: raise self.failureException(msg or "%r is %r" % (first, second)) def failUnlessIn(self, first, second, msg=None): """Assert that C{first} is contained in C{second}.""" if first not in second: raise self.failureException(msg or "%r not in %r" % (first, second)) def failUnlessStartsWith(self, first, second, msg=None): """Assert that C{first} starts with C{second}.""" if first[:len(second)] != second: raise self.failureException(msg or "%r doesn't start with %r" % (first, second)) def failIfStartsWith(self, first, second, msg=None): """Assert that C{first} doesn't start with C{second}.""" if first[:len(second)] == second: raise self.failureException(msg or "%r starts with %r" % (first, second)) def failUnlessEndsWith(self, first, second, msg=None): """Assert that C{first} starts with C{second}.""" if first[len(first)-len(second):] != second: raise self.failureException(msg or "%r doesn't end with %r" % (first, second)) def failIfEndsWith(self, first, second, msg=None): """Assert that C{first} doesn't start with C{second}.""" if first[len(first)-len(second):] == second: raise self.failureException(msg or "%r ends with %r" % (first, second)) def failIfIn(self, first, second, msg=None): """Assert that C{first} is not contained in C{second}.""" if first in second: raise self.failureException(msg or "%r in %r" % (first, second)) def failUnlessApproximates(self, first, second, tolerance, msg=None): """Assert that C{first} is near C{second} by at most C{tolerance}.""" if abs(first - second) > tolerance: raise self.failureException(msg or "abs(%r - %r) > %r" % (first, second, tolerance)) def failIfApproximates(self, first, second, tolerance, msg=None): """Assert that C{first} is far from C{second} by at least C{tolerance}. """ if abs(first - second) <= tolerance: raise self.failureException(msg or "abs(%r - %r) <= %r" % (first, second, tolerance)) def failUnlessMethodsMatch(self, first, second): """Assert that public methods in C{first} are present in C{second}. This method asserts that all public methods found in C{first} are also present in C{second} and accept the same arguments. C{first} may have its own private methods, though, and may not have all methods found in C{second}. Note that if a private method in C{first} matches the name of one in C{second}, their specification is still compared. This is useful to verify if a fake or stub class have the same API as the real class being simulated. """ first_methods = dict(inspect.getmembers(first, inspect.ismethod)) second_methods = dict(inspect.getmembers(second, inspect.ismethod)) for name, first_method in first_methods.iteritems(): first_argspec = inspect.getargspec(first_method) first_formatted = inspect.formatargspec(*first_argspec) second_method = second_methods.get(name) if second_method is None: if name[:1] == "_": continue # First may have its own private methods. raise self.failureException("%s.%s%s not present in %s" % (first.__name__, name, first_formatted, second.__name__)) second_argspec = inspect.getargspec(second_method) if first_argspec != second_argspec: second_formatted = inspect.formatargspec(*second_argspec) raise self.failureException("%s.%s%s != %s.%s%s" % (first.__name__, name, first_formatted, second.__name__, name, second_formatted)) def failUnlessRaises(self, excClass, callableObj, *args, **kwargs): """ Fail unless an exception of class excClass is thrown by callableObj when invoked with arguments args and keyword arguments kwargs. If a different type of exception is thrown, it will not be caught, and the test case will be deemed to have suffered an error, exactly as for an unexpected exception. It returns the exception instance if it matches the given exception class. """ try: result = callableObj(*args, **kwargs) except excClass, e: return e else: excName = excClass if hasattr(excClass, "__name__"): excName = excClass.__name__ raise self.failureException( "%s not raised (%r returned)" % (excName, result)) assertIs = failUnlessIs assertIsNot = failIfIs assertIn = failUnlessIn assertNotIn = failIfIn assertStartsWith = failUnlessStartsWith assertNotStartsWith = failIfStartsWith assertEndsWith = failUnlessEndsWith assertNotEndsWith = failIfEndsWith assertApproximates = failUnlessApproximates assertNotApproximates = failIfApproximates assertMethodsMatch = failUnlessMethodsMatch assertRaises = failUnlessRaises # The following are missing in Python < 2.4. assertTrue = unittest.TestCase.failUnless assertFalse = unittest.TestCase.failIf # The following is provided for compatibility with Twisted's trial. assertIdentical = assertIs assertNotIdentical = assertIsNot failUnlessIdentical = failUnlessIs failIfIdentical = failIfIs # -------------------------------------------------------------------- # Mocker. class classinstancemethod(object): def __init__(self, method): self.method = method def __get__(self, obj, cls=None): def bound_method(*args, **kwargs): return self.method(cls, obj, *args, **kwargs) return bound_method class MockerBase(object): """Controller of mock objects. A mocker instance is used to command recording and replay of expectations on any number of mock objects. Expectations should be expressed for the mock object while in record mode (the initial one) by using the mock object itself, and using the mocker (and/or C{expect()} as a helper) to define additional behavior for each event. For instance:: mock = mocker.mock() mock.hello() mocker.result("Hi!") mocker.replay() assert mock.hello() == "Hi!" mock.restore() mock.verify() In this short excerpt a mock object is being created, then an expectation of a call to the C{hello()} method was recorded, and when called the method should return the value C{10}. Then, the mocker is put in replay mode, and the expectation is satisfied by calling the C{hello()} method, which indeed returns 10. Finally, a call to the L{restore()} method is performed to undo any needed changes made in the environment, and the L{verify()} method is called to ensure that all defined expectations were met. The same logic can be expressed more elegantly using the C{with mocker:} statement, as follows:: mock = mocker.mock() mock.hello() mocker.result("Hi!") with mocker: assert mock.hello() == "Hi!" Also, the MockerTestCase class, which integrates the mocker on a unittest.TestCase subclass, may be used to reduce the overhead of controlling the mocker. A test could be written as follows:: class SampleTest(MockerTestCase): def test_hello(self): mock = self.mocker.mock() mock.hello() self.mocker.result("Hi!") self.mocker.replay() self.assertEquals(mock.hello(), "Hi!") """ _recorders = [] # For convenience only. on = expect class __metaclass__(type): def __init__(self, name, bases, dict): # Make independent lists on each subclass, inheriting from parent. self._recorders = list(getattr(self, "_recorders", ())) def __init__(self): self._recorders = self._recorders[:] self._events = [] self._recording = True self._ordering = False self._last_orderer = None def is_recording(self): """Return True if in recording mode, False if in replay mode. Recording is the initial state. """ return self._recording def replay(self): """Change to replay mode, where recorded events are reproduced. If already in replay mode, the mocker will be restored, with all expectations reset, and then put again in replay mode. An alternative and more comfortable way to replay changes is using the 'with' statement, as follows:: mocker = Mocker() with mocker: The 'with' statement will automatically put mocker in replay mode, and will also verify if all events were correctly reproduced at the end (using L{verify()}), and also restore any changes done in the environment (with L{restore()}). Also check the MockerTestCase class, which integrates the unittest.TestCase class with mocker. """ if not self._recording: for event in self._events: event.restore() else: self._recording = False for event in self._events: event.replay() def restore(self): """Restore changes in the environment, and return to recording mode. This should always be called after the test is complete (succeeding or not). There are ways to call this method automatically on completion (e.g. using a C{with mocker:} statement, or using the L{MockerTestCase} class. """ if not self._recording: self._recording = True for event in self._events: event.restore() def reset(self): """Reset the mocker state. This will restore environment changes, if currently in replay mode, and then remove all events previously recorded. """ if not self._recording: self.restore() self.unorder() del self._events[:] def get_events(self): """Return all recorded events.""" return self._events[:] def add_event(self, event): """Add an event. This method is used internally by the implementation, and shouldn't be needed on normal mocker usage. """ self._events.append(event) if self._ordering: orderer = event.add_task(Orderer(event.path)) if self._last_orderer: orderer.add_dependency(self._last_orderer) self._last_orderer = orderer return event def verify(self): """Check if all expectations were met, and raise AssertionError if not. The exception message will include a nice description of which expectations were not met, and why. """ errors = [] for event in self._events: try: event.verify() except AssertionError, e: error = str(e) if not error: raise RuntimeError("Empty error message from %r" % event) errors.append(error) if errors: message = [ERROR_PREFIX + "Unmet expectations:", ""] for error in errors: lines = error.splitlines() message.append("=> " + lines.pop(0)) message.extend([" " + line for line in lines]) message.append("") raise AssertionError(os.linesep.join(message)) def mock(self, spec_and_type=None, spec=None, type=None, name=None, count=True): """Return a new mock object. @param spec_and_type: Handy positional argument which sets both spec and type. @param spec: Method calls will be checked for correctness against the given class. @param type: If set, the Mock's __class__ attribute will return the given type. This will make C{isinstance()} calls on the object work. @param name: Name for the mock object, used in the representation of expressions. The name is rarely needed, as it's usually guessed correctly from the variable name used. @param count: If set to false, expressions may be executed any number of times, unless an expectation is explicitly set using the L{count()} method. By default, expressions are expected once. """ if spec_and_type is not None: spec = type = spec_and_type return Mock(self, spec=spec, type=type, name=name, count=count) def proxy(self, object, spec=True, type=True, name=None, count=True, passthrough=True): """Return a new mock object which proxies to the given object. Proxies are useful when only part of the behavior of an object is to be mocked. Unknown expressions may be passed through to the real implementation implicitly (if the C{passthrough} argument is True), or explicitly (using the L{passthrough()} method on the event). @param object: Real object to be proxied, and replaced by the mock on replay mode. It may also be an "import path", such as C{"time.time"}, in which case the object will be the C{time} function from the C{time} module. @param spec: Method calls will be checked for correctness against the given object, which may be a class or an instance where attributes will be looked up. Defaults to the the C{object} parameter. May be set to None explicitly, in which case spec checking is disabled. Checks may also be disabled explicitly on a per-event basis with the L{nospec()} method. @param type: If set, the Mock's __class__ attribute will return the given type. This will make C{isinstance()} calls on the object work. Defaults to the type of the C{object} parameter. May be set to None explicitly. @param name: Name for the mock object, used in the representation of expressions. The name is rarely needed, as it's usually guessed correctly from the variable name used. @param count: If set to false, expressions may be executed any number of times, unless an expectation is explicitly set using the L{count()} method. By default, expressions are expected once. @param passthrough: If set to False, passthrough of actions on the proxy to the real object will only happen when explicitly requested via the L{passthrough()} method. """ if isinstance(object, basestring): if name is None: name = object import_stack = object.split(".") attr_stack = [] while import_stack: module_path = ".".join(import_stack) try: __import__(module_path) except ImportError: attr_stack.insert(0, import_stack.pop()) if not import_stack: raise continue else: object = sys.modules[module_path] for attr in attr_stack: object = getattr(object, attr) break if isinstance(object, types.UnboundMethodType): object = object.im_func if spec is True: spec = object if type is True: type = __builtin__.type(object) return Mock(self, spec=spec, type=type, object=object, name=name, count=count, passthrough=passthrough) def replace(self, object, spec=True, type=True, name=None, count=True, passthrough=True): """Create a proxy, and replace the original object with the mock. On replay, the original object will be replaced by the returned proxy in all dictionaries found in the running interpreter via the garbage collecting system. This should cover module namespaces, class namespaces, instance namespaces, and so on. @param object: Real object to be proxied, and replaced by the mock on replay mode. It may also be an "import path", such as C{"time.time"}, in which case the object will be the C{time} function from the C{time} module. @param spec: Method calls will be checked for correctness against the given object, which may be a class or an instance where attributes will be looked up. Defaults to the the C{object} parameter. May be set to None explicitly, in which case spec checking is disabled. Checks may also be disabled explicitly on a per-event basis with the L{nospec()} method. @param type: If set, the Mock's __class__ attribute will return the given type. This will make C{isinstance()} calls on the object work. Defaults to the type of the C{object} parameter. May be set to None explicitly. @param name: Name for the mock object, used in the representation of expressions. The name is rarely needed, as it's usually guessed correctly from the variable name used. @param passthrough: If set to False, passthrough of actions on the proxy to the real object will only happen when explicitly requested via the L{passthrough()} method. """ mock = self.proxy(object, spec, type, name, count, passthrough) event = self._get_replay_restore_event() event.add_task(ProxyReplacer(mock)) return mock def patch(self, object, spec=True): """Patch an existing object to reproduce recorded events. @param object: Class or instance to be patched. @param spec: Method calls will be checked for correctness against the given object, which may be a class or an instance where attributes will be looked up. Defaults to the the C{object} parameter. May be set to None explicitly, in which case spec checking is disabled. Checks may also be disabled explicitly on a per-event basis with the L{nospec()} method. The result of this method is still a mock object, which can be used like any other mock object to record events. The difference is that when the mocker is put on replay mode, the *real* object will be modified to behave according to recorded expectations. Patching works in individual instances, and also in classes. When an instance is patched, recorded events will only be considered on this specific instance, and other instances should behave normally. When a class is patched, the reproduction of events will be considered on any instance of this class once created (collectively). Observe that, unlike with proxies which catch only events done through the mock object, *all* accesses to recorded expectations will be considered; even these coming from the object itself (e.g. C{self.hello()} is considered if this method was patched). While this is a very powerful feature, and many times the reason to use patches in the first place, it's important to keep this behavior in mind. Patching of the original object only takes place when the mocker is put on replay mode, and the patched object will be restored to its original state once the L{restore()} method is called (explicitly, or implicitly with alternative conventions, such as a C{with mocker:} block, or a MockerTestCase class). """ if spec is True: spec = object patcher = Patcher() event = self._get_replay_restore_event() event.add_task(patcher) mock = Mock(self, object=object, patcher=patcher, passthrough=True, spec=spec) patcher.patch_attr(object, '__mocker_mock__', mock) return mock def act(self, path): """This is called by mock objects whenever something happens to them. This method is part of the implementation between the mocker and mock objects. """ if self._recording: event = self.add_event(Event(path)) for recorder in self._recorders: recorder(self, event) return Mock(self, path) else: # First run events that may run, then run unsatisfied events, then # ones not previously run. We put the index in the ordering tuple # instead of the actual event because we want a stable sort # (ordering between 2 events is undefined). events = self._events order = [(events[i].satisfied()*2 + events[i].has_run(), i) for i in range(len(events))] order.sort() postponed = None for weight, i in order: event = events[i] if event.matches(path): if event.may_run(path): return event.run(path) elif postponed is None: postponed = event if postponed is not None: return postponed.run(path) raise MatchError(ERROR_PREFIX + "Unexpected expression: %s" % path) def get_recorders(cls, self): """Return recorders associated with this mocker class or instance. This method may be called on mocker instances and also on mocker classes. See the L{add_recorder()} method for more information. """ return (self or cls)._recorders[:] get_recorders = classinstancemethod(get_recorders) def add_recorder(cls, self, recorder): """Add a recorder to this mocker class or instance. @param recorder: Callable accepting C{(mocker, event)} as parameters. This is part of the implementation of mocker. All registered recorders are called for translating events that happen during recording into expectations to be met once the state is switched to replay mode. This method may be called on mocker instances and also on mocker classes. When called on a class, the recorder will be used by all instances, and also inherited on subclassing. When called on instances, the recorder is added only to the given instance. """ (self or cls)._recorders.append(recorder) return recorder add_recorder = classinstancemethod(add_recorder) def remove_recorder(cls, self, recorder): """Remove the given recorder from this mocker class or instance. This method may be called on mocker classes and also on mocker instances. See the L{add_recorder()} method for more information. """ (self or cls)._recorders.remove(recorder) remove_recorder = classinstancemethod(remove_recorder) def result(self, value): """Make the last recorded event return the given value on replay. @param value: Object to be returned when the event is replayed. """ self.call(lambda *args, **kwargs: value) def generate(self, sequence): """Last recorded event will return a generator with the given sequence. @param sequence: Sequence of values to be generated. """ def generate(*args, **kwargs): for value in sequence: yield value self.call(generate) def throw(self, exception): """Make the last recorded event raise the given exception on replay. @param exception: Class or instance of exception to be raised. """ def raise_exception(*args, **kwargs): raise exception self.call(raise_exception) def call(self, func): """Make the last recorded event cause the given function to be called. @param func: Function to be called. The result of the function will be used as the event result. """ self._events[-1].add_task(FunctionRunner(func)) def count(self, min, max=False): """Last recorded event must be replayed between min and max times. @param min: Minimum number of times that the event must happen. @param max: Maximum number of times that the event must happen. If not given, it defaults to the same value of the C{min} parameter. If set to None, there is no upper limit, and the expectation is met as long as it happens at least C{min} times. """ event = self._events[-1] for task in event.get_tasks(): if isinstance(task, RunCounter): event.remove_task(task) event.add_task(RunCounter(min, max)) def is_ordering(self): """Return true if all events are being ordered. See the L{order()} method. """ return self._ordering def unorder(self): """Disable the ordered mode. See the L{order()} method for more information. """ self._ordering = False self._last_orderer = None def order(self, *path_holders): """Create an expectation of order between two or more events. @param path_holders: Objects returned as the result of recorded events. By default, mocker won't force events to happen precisely in the order they were recorded. Calling this method will change this behavior so that events will only match if reproduced in the correct order. There are two ways in which this method may be used. Which one is used in a given occasion depends only on convenience. If no arguments are passed, the mocker will be put in a mode where all the recorded events following the method call will only be met if they happen in order. When that's used, the mocker may be put back in unordered mode by calling the L{unorder()} method, or by using a 'with' block, like so:: with mocker.ordered(): In this case, only expressions in will be ordered, and the mocker will be back in unordered mode after the 'with' block. The second way to use it is by specifying precisely which events should be ordered. As an example:: mock = mocker.mock() expr1 = mock.hello() expr2 = mock.world expr3 = mock.x.y.z mocker.order(expr1, expr2, expr3) This method of ordering only works when the expression returns another object. Also check the L{after()} and L{before()} methods, which are alternative ways to perform this. """ if not path_holders: self._ordering = True return OrderedContext(self) last_orderer = None for path_holder in path_holders: if type(path_holder) is Path: path = path_holder else: path = path_holder.__mocker_path__ for event in self._events: if event.path is path: for task in event.get_tasks(): if isinstance(task, Orderer): orderer = task break else: orderer = Orderer(path) event.add_task(orderer) if last_orderer: orderer.add_dependency(last_orderer) last_orderer = orderer break def after(self, *path_holders): """Last recorded event must happen after events referred to. @param path_holders: Objects returned as the result of recorded events which should happen before the last recorded event As an example, the idiom:: expect(mock.x).after(mock.y, mock.z) is an alternative way to say:: expr_x = mock.x expr_y = mock.y expr_z = mock.z mocker.order(expr_y, expr_x) mocker.order(expr_z, expr_x) See L{order()} for more information. """ last_path = self._events[-1].path for path_holder in path_holders: self.order(path_holder, last_path) def before(self, *path_holders): """Last recorded event must happen before events referred to. @param path_holders: Objects returned as the result of recorded events which should happen after the last recorded event As an example, the idiom:: expect(mock.x).before(mock.y, mock.z) is an alternative way to say:: expr_x = mock.x expr_y = mock.y expr_z = mock.z mocker.order(expr_x, expr_y) mocker.order(expr_x, expr_z) See L{order()} for more information. """ last_path = self._events[-1].path for path_holder in path_holders: self.order(last_path, path_holder) def nospec(self): """Don't check method specification of real object on last event. By default, when using a mock created as the result of a call to L{proxy()}, L{replace()}, and C{patch()}, or when passing the spec attribute to the L{mock()} method, method calls on the given object are checked for correctness against the specification of the real object (or the explicitly provided spec). This method will disable that check specifically for the last recorded event. """ event = self._events[-1] for task in event.get_tasks(): if isinstance(task, SpecChecker): event.remove_task(task) def passthrough(self, result_callback=None): """Make the last recorded event run on the real object once seen. @param result_callback: If given, this function will be called with the result of the *real* method call as the only argument. This can only be used on proxies, as returned by the L{proxy()} and L{replace()} methods, or on mocks representing patched objects, as returned by the L{patch()} method. """ event = self._events[-1] if event.path.root_object is None: raise TypeError("Mock object isn't a proxy") event.add_task(PathExecuter(result_callback)) def __enter__(self): """Enter in a 'with' context. This will run replay().""" self.replay() return self def __exit__(self, type, value, traceback): """Exit from a 'with' context. This will run restore() at all times, but will only run verify() if the 'with' block itself hasn't raised an exception. Exceptions in that block are never swallowed. """ self.restore() if type is None: self.verify() return False def _get_replay_restore_event(self): """Return unique L{ReplayRestoreEvent}, creating if needed. Some tasks only want to replay/restore. When that's the case, they shouldn't act on other events during replay. Also, they can all be put in a single event when that's the case. Thus, we add a single L{ReplayRestoreEvent} as the first element of the list. """ if not self._events or type(self._events[0]) != ReplayRestoreEvent: self._events.insert(0, ReplayRestoreEvent()) return self._events[0] class OrderedContext(object): def __init__(self, mocker): self._mocker = mocker def __enter__(self): return None def __exit__(self, type, value, traceback): self._mocker.unorder() class Mocker(MockerBase): __doc__ = MockerBase.__doc__ # Decorator to add recorders on the standard Mocker class. recorder = Mocker.add_recorder # -------------------------------------------------------------------- # Mock object. class Mock(object): def __init__(self, mocker, path=None, name=None, spec=None, type=None, object=None, passthrough=False, patcher=None, count=True): self.__mocker__ = mocker self.__mocker_path__ = path or Path(self, object) self.__mocker_name__ = name self.__mocker_spec__ = spec self.__mocker_object__ = object self.__mocker_passthrough__ = passthrough self.__mocker_patcher__ = patcher self.__mocker_replace__ = False self.__mocker_type__ = type self.__mocker_count__ = count def __mocker_act__(self, kind, args=(), kwargs={}, object=None): if self.__mocker_name__ is None: self.__mocker_name__ = find_object_name(self, 2) action = Action(kind, args, kwargs, self.__mocker_path__) path = self.__mocker_path__ + action if object is not None: path.root_object = object try: return self.__mocker__.act(path) except MatchError, exception: root_mock = path.root_mock if (path.root_object is not None and root_mock.__mocker_passthrough__): return path.execute(path.root_object) # Reinstantiate to show raise statement on traceback, and # also to make the traceback shown shorter. raise MatchError(str(exception)) except AssertionError, e: lines = str(e).splitlines() message = [ERROR_PREFIX + "Unmet expectation:", ""] message.append("=> " + lines.pop(0)) message.extend([" " + line for line in lines]) message.append("") raise AssertionError(os.linesep.join(message)) def __getattribute__(self, name): if name.startswith("__mocker_"): return super(Mock, self).__getattribute__(name) if name == "__class__": if self.__mocker__.is_recording() or self.__mocker_type__ is None: return type(self) return self.__mocker_type__ if name == "__length_hint__": # This is used by Python 2.6+ to optimize the allocation # of arrays in certain cases. Pretend it doesn't exist. raise AttributeError("No __length_hint__ here!") return self.__mocker_act__("getattr", (name,)) def __setattr__(self, name, value): if name.startswith("__mocker_"): return super(Mock, self).__setattr__(name, value) return self.__mocker_act__("setattr", (name, value)) def __delattr__(self, name): return self.__mocker_act__("delattr", (name,)) def __call__(self, *args, **kwargs): return self.__mocker_act__("call", args, kwargs) def __contains__(self, value): return self.__mocker_act__("contains", (value,)) def __getitem__(self, key): return self.__mocker_act__("getitem", (key,)) def __setitem__(self, key, value): return self.__mocker_act__("setitem", (key, value)) def __delitem__(self, key): return self.__mocker_act__("delitem", (key,)) def __len__(self): # MatchError is turned on an AttributeError so that list() and # friends act properly when trying to get length hints on # something that doesn't offer them. try: result = self.__mocker_act__("len") except MatchError, e: raise AttributeError(str(e)) if type(result) is Mock: return 0 return result def __nonzero__(self): try: result = self.__mocker_act__("nonzero") except MatchError, e: return True if type(result) is Mock: return True return result def __iter__(self): # XXX On py3k, when next() becomes __next__(), we'll be able # to return the mock itself because it will be considered # an iterator (we'll be mocking __next__ as well, which we # can't now). result = self.__mocker_act__("iter") if type(result) is Mock: return iter([]) return result # When adding a new action kind here, also add support for it on # Action.execute() and Path.__str__(). def find_object_name(obj, depth=0): """Try to detect how the object is named on a previous scope.""" try: frame = sys._getframe(depth+1) except: return None for name, frame_obj in frame.f_locals.iteritems(): if frame_obj is obj: return name self = frame.f_locals.get("self") if self is not None: try: items = list(self.__dict__.iteritems()) except: pass else: for name, self_obj in items: if self_obj is obj: return name return None # -------------------------------------------------------------------- # Action and path. class Action(object): def __init__(self, kind, args, kwargs, path=None): self.kind = kind self.args = args self.kwargs = kwargs self.path = path self._execute_cache = {} def __repr__(self): if self.path is None: return "Action(%r, %r, %r)" % (self.kind, self.args, self.kwargs) return "Action(%r, %r, %r, %r)" % \ (self.kind, self.args, self.kwargs, self.path) def __eq__(self, other): return (self.kind == other.kind and self.args == other.args and self.kwargs == other.kwargs) def __ne__(self, other): return not self.__eq__(other) def matches(self, other): return (self.kind == other.kind and match_params(self.args, self.kwargs, other.args, other.kwargs)) def execute(self, object): # This caching scheme may fail if the object gets deallocated before # the action, as the id might get reused. It's somewhat easy to fix # that with a weakref callback. For our uses, though, the object # should never get deallocated before the action itself, so we'll # just keep it simple. if id(object) in self._execute_cache: return self._execute_cache[id(object)] execute = getattr(object, "__mocker_execute__", None) if execute is not None: result = execute(self, object) else: kind = self.kind if kind == "getattr": result = getattr(object, self.args[0]) elif kind == "setattr": result = setattr(object, self.args[0], self.args[1]) elif kind == "delattr": result = delattr(object, self.args[0]) elif kind == "call": result = object(*self.args, **self.kwargs) elif kind == "contains": result = self.args[0] in object elif kind == "getitem": result = object[self.args[0]] elif kind == "setitem": result = object[self.args[0]] = self.args[1] elif kind == "delitem": del object[self.args[0]] result = None elif kind == "len": result = len(object) elif kind == "nonzero": result = bool(object) elif kind == "iter": result = iter(object) else: raise RuntimeError("Don't know how to execute %r kind." % kind) self._execute_cache[id(object)] = result return result class Path(object): def __init__(self, root_mock, root_object=None, actions=()): self.root_mock = root_mock self.root_object = root_object self.actions = tuple(actions) self.__mocker_replace__ = False def parent_path(self): if not self.actions: return None return self.actions[-1].path parent_path = property(parent_path) def __add__(self, action): """Return a new path which includes the given action at the end.""" return self.__class__(self.root_mock, self.root_object, self.actions + (action,)) def __eq__(self, other): """Verify if the two paths are equal. Two paths are equal if they refer to the same mock object, and have the actions with equal kind, args and kwargs. """ if (self.root_mock is not other.root_mock or self.root_object is not other.root_object or len(self.actions) != len(other.actions)): return False for action, other_action in zip(self.actions, other.actions): if action != other_action: return False return True def matches(self, other): """Verify if the two paths are equivalent. Two paths are equal if they refer to the same mock object, and have the same actions performed on them. """ if (self.root_mock is not other.root_mock or len(self.actions) != len(other.actions)): return False for action, other_action in zip(self.actions, other.actions): if not action.matches(other_action): return False return True def execute(self, object): """Execute all actions sequentially on object, and return result. """ for action in self.actions: object = action.execute(object) return object def __str__(self): """Transform the path into a nice string such as obj.x.y('z').""" result = self.root_mock.__mocker_name__ or "" for action in self.actions: if action.kind == "getattr": result = "%s.%s" % (result, action.args[0]) elif action.kind == "setattr": result = "%s.%s = %r" % (result, action.args[0], action.args[1]) elif action.kind == "delattr": result = "del %s.%s" % (result, action.args[0]) elif action.kind == "call": args = [repr(x) for x in action.args] items = list(action.kwargs.iteritems()) items.sort() for pair in items: args.append("%s=%r" % pair) result = "%s(%s)" % (result, ", ".join(args)) elif action.kind == "contains": result = "%r in %s" % (action.args[0], result) elif action.kind == "getitem": result = "%s[%r]" % (result, action.args[0]) elif action.kind == "setitem": result = "%s[%r] = %r" % (result, action.args[0], action.args[1]) elif action.kind == "delitem": result = "del %s[%r]" % (result, action.args[0]) elif action.kind == "len": result = "len(%s)" % result elif action.kind == "nonzero": result = "bool(%s)" % result elif action.kind == "iter": result = "iter(%s)" % result else: raise RuntimeError("Don't know how to format kind %r" % action.kind) return result class SpecialArgument(object): """Base for special arguments for matching parameters.""" def __init__(self, object=None): self.object = object def __repr__(self): if self.object is None: return self.__class__.__name__ else: return "%s(%r)" % (self.__class__.__name__, self.object) def matches(self, other): return True def __eq__(self, other): return type(other) == type(self) and self.object == other.object class ANY(SpecialArgument): """Matches any single argument.""" ANY = ANY() class ARGS(SpecialArgument): """Matches zero or more positional arguments.""" ARGS = ARGS() class KWARGS(SpecialArgument): """Matches zero or more keyword arguments.""" KWARGS = KWARGS() class IS(SpecialArgument): def matches(self, other): return self.object is other def __eq__(self, other): return type(other) == type(self) and self.object is other.object class CONTAINS(SpecialArgument): def matches(self, other): try: other.__contains__ except AttributeError: try: iter(other) except TypeError: # If an object can't be iterated, and has no __contains__ # hook, it'd blow up on the test below. We test this in # advance to prevent catching more errors than we really # want. return False return self.object in other class IN(SpecialArgument): def matches(self, other): return other in self.object class MATCH(SpecialArgument): def matches(self, other): return bool(self.object(other)) def __eq__(self, other): return type(other) == type(self) and self.object is other.object def match_params(args1, kwargs1, args2, kwargs2): """Match the two sets of parameters, considering special parameters.""" has_args = ARGS in args1 has_kwargs = KWARGS in args1 if has_kwargs: args1 = [arg1 for arg1 in args1 if arg1 is not KWARGS] elif len(kwargs1) != len(kwargs2): return False if not has_args and len(args1) != len(args2): return False # Either we have the same number of kwargs, or unknown keywords are # accepted (KWARGS was used), so check just the ones in kwargs1. for key, arg1 in kwargs1.iteritems(): if key not in kwargs2: return False arg2 = kwargs2[key] if isinstance(arg1, SpecialArgument): if not arg1.matches(arg2): return False elif arg1 != arg2: return False # Keywords match. Now either we have the same number of # arguments, or ARGS was used. If ARGS wasn't used, arguments # must match one-on-one necessarily. if not has_args: for arg1, arg2 in zip(args1, args2): if isinstance(arg1, SpecialArgument): if not arg1.matches(arg2): return False elif arg1 != arg2: return False return True # Easy choice. Keywords are matching, and anything on args is accepted. if (ARGS,) == args1: return True # We have something different there. If we don't have positional # arguments on the original call, it can't match. if not args2: # Unless we have just several ARGS (which is bizarre, but..). for arg1 in args1: if arg1 is not ARGS: return False return True # Ok, all bets are lost. We have to actually do the more expensive # matching. This is an algorithm based on the idea of the Levenshtein # Distance between two strings, but heavily hacked for this purpose. args2l = len(args2) if args1[0] is ARGS: args1 = args1[1:] array = [0]*args2l else: array = [1]*args2l for i in range(len(args1)): last = array[0] if args1[i] is ARGS: for j in range(1, args2l): last, array[j] = array[j], min(array[j-1], array[j], last) else: array[0] = i or int(args1[i] != args2[0]) for j in range(1, args2l): last, array[j] = array[j], last or int(args1[i] != args2[j]) if 0 not in array: return False if array[-1] != 0: return False return True # -------------------------------------------------------------------- # Event and task base. class Event(object): """Aggregation of tasks that keep track of a recorded action. An event represents something that may or may not happen while the mocked environment is running, such as an attribute access, or a method call. The event is composed of several tasks that are orchestrated together to create a composed meaning for the event, including for which actions it should be run, what happens when it runs, and what's the expectations about the actions run. """ def __init__(self, path=None): self.path = path self._tasks = [] self._has_run = False def add_task(self, task): """Add a new task to this taks.""" self._tasks.append(task) return task def remove_task(self, task): self._tasks.remove(task) def get_tasks(self): return self._tasks[:] def matches(self, path): """Return true if *all* tasks match the given path.""" for task in self._tasks: if not task.matches(path): return False return bool(self._tasks) def has_run(self): return self._has_run def may_run(self, path): """Verify if any task would certainly raise an error if run. This will call the C{may_run()} method on each task and return false if any of them returns false. """ for task in self._tasks: if not task.may_run(path): return False return True def run(self, path): """Run all tasks with the given action. @param path: The path of the expression run. Running an event means running all of its tasks individually and in order. An event should only ever be run if all of its tasks claim to match the given action. The result of this method will be the last result of a task which isn't None, or None if they're all None. """ self._has_run = True result = None errors = [] for task in self._tasks: try: task_result = task.run(path) except AssertionError, e: error = str(e) if not error: raise RuntimeError("Empty error message from %r" % task) errors.append(error) else: if task_result is not None: result = task_result if errors: message = [str(self.path)] if str(path) != message[0]: message.append("- Run: %s" % path) for error in errors: lines = error.splitlines() message.append("- " + lines.pop(0)) message.extend([" " + line for line in lines]) raise AssertionError(os.linesep.join(message)) return result def satisfied(self): """Return true if all tasks are satisfied. Being satisfied means that there are no unmet expectations. """ for task in self._tasks: try: task.verify() except AssertionError: return False return True def verify(self): """Run verify on all tasks. The verify method is supposed to raise an AssertionError if the task has unmet expectations, with a one-line explanation about why this item is unmet. This method should be safe to be called multiple times without side effects. """ errors = [] for task in self._tasks: try: task.verify() except AssertionError, e: error = str(e) if not error: raise RuntimeError("Empty error message from %r" % task) errors.append(error) if errors: message = [str(self.path)] for error in errors: lines = error.splitlines() message.append("- " + lines.pop(0)) message.extend([" " + line for line in lines]) raise AssertionError(os.linesep.join(message)) def replay(self): """Put all tasks in replay mode.""" self._has_run = False for task in self._tasks: task.replay() def restore(self): """Restore the state of all tasks.""" for task in self._tasks: task.restore() class ReplayRestoreEvent(Event): """Helper event for tasks which need replay/restore but shouldn't match.""" def matches(self, path): return False class Task(object): """Element used to track one specific aspect on an event. A task is responsible for adding any kind of logic to an event. Examples of that are counting the number of times the event was made, verifying parameters if any, and so on. """ def matches(self, path): """Return true if the task is supposed to be run for the given path. """ return True def may_run(self, path): """Return false if running this task would certainly raise an error.""" return True def run(self, path): """Perform the task item, considering that the given action happened. """ def verify(self): """Raise AssertionError if expectations for this item are unmet. The verify method is supposed to raise an AssertionError if the task has unmet expectations, with a one-line explanation about why this item is unmet. This method should be safe to be called multiple times without side effects. """ def replay(self): """Put the task in replay mode. Any expectations of the task should be reset. """ def restore(self): """Restore any environmental changes made by the task. Verify should continue to work after this is called. """ # -------------------------------------------------------------------- # Task implementations. class OnRestoreCaller(Task): """Call a given callback when restoring.""" def __init__(self, callback): self._callback = callback def restore(self): self._callback() class PathMatcher(Task): """Match the action path against a given path.""" def __init__(self, path): self.path = path def matches(self, path): return self.path.matches(path) def path_matcher_recorder(mocker, event): event.add_task(PathMatcher(event.path)) Mocker.add_recorder(path_matcher_recorder) class RunCounter(Task): """Task which verifies if the number of runs are within given boundaries. """ def __init__(self, min, max=False): self.min = min if max is None: self.max = sys.maxint elif max is False: self.max = min else: self.max = max self._runs = 0 def replay(self): self._runs = 0 def may_run(self, path): return self._runs < self.max def run(self, path): self._runs += 1 if self._runs > self.max: self.verify() def verify(self): if not self.min <= self._runs <= self.max: if self._runs < self.min: raise AssertionError("Performed fewer times than expected.") raise AssertionError("Performed more times than expected.") class ImplicitRunCounter(RunCounter): """RunCounter inserted by default on any event. This is a way to differentiate explicitly added counters and implicit ones. """ def run_counter_recorder(mocker, event): """Any event may be repeated once, unless disabled by default.""" if event.path.root_mock.__mocker_count__: event.add_task(ImplicitRunCounter(1)) Mocker.add_recorder(run_counter_recorder) def run_counter_removal_recorder(mocker, event): """ Events created by getattr actions which lead to other events may be repeated any number of times. For that, we remove implicit run counters of any getattr actions leading to the current one. """ parent_path = event.path.parent_path for event in mocker.get_events()[::-1]: if (event.path is parent_path and event.path.actions[-1].kind == "getattr"): for task in event.get_tasks(): if type(task) is ImplicitRunCounter: event.remove_task(task) Mocker.add_recorder(run_counter_removal_recorder) class MockReturner(Task): """Return a mock based on the action path.""" def __init__(self, mocker): self.mocker = mocker def run(self, path): return Mock(self.mocker, path) def mock_returner_recorder(mocker, event): """Events that lead to other events must return mock objects.""" parent_path = event.path.parent_path for event in mocker.get_events(): if event.path is parent_path: for task in event.get_tasks(): if isinstance(task, MockReturner): break else: event.add_task(MockReturner(mocker)) break Mocker.add_recorder(mock_returner_recorder) class FunctionRunner(Task): """Task that runs a function everything it's run. Arguments of the last action in the path are passed to the function, and the function result is also returned. """ def __init__(self, func): self._func = func def run(self, path): action = path.actions[-1] return self._func(*action.args, **action.kwargs) class PathExecuter(Task): """Task that executes a path in the real object, and returns the result.""" def __init__(self, result_callback=None): self._result_callback = result_callback def get_result_callback(self): return self._result_callback def run(self, path): result = path.execute(path.root_object) if self._result_callback is not None: self._result_callback(result) return result class Orderer(Task): """Task to establish an order relation between two events. An orderer task will only match once all its dependencies have been run. """ def __init__(self, path): self.path = path self._run = False self._dependencies = [] def replay(self): self._run = False def has_run(self): return self._run def may_run(self, path): for dependency in self._dependencies: if not dependency.has_run(): return False return True def run(self, path): for dependency in self._dependencies: if not dependency.has_run(): raise AssertionError("Should be after: %s" % dependency.path) self._run = True def add_dependency(self, orderer): self._dependencies.append(orderer) def get_dependencies(self): return self._dependencies class SpecChecker(Task): """Task to check if arguments of the last action conform to a real method. """ def __init__(self, method): self._method = method self._unsupported = False if method: try: self._args, self._varargs, self._varkwargs, self._defaults = \ inspect.getargspec(method) except TypeError: self._unsupported = True else: if self._defaults is None: self._defaults = () if type(method) is type(self.run): self._args = self._args[1:] def get_method(self): return self._method def _raise(self, message): spec = inspect.formatargspec(self._args, self._varargs, self._varkwargs, self._defaults) raise AssertionError("Specification is %s%s: %s" % (self._method.__name__, spec, message)) def verify(self): if not self._method: raise AssertionError("Method not found in real specification") def may_run(self, path): try: self.run(path) except AssertionError: return False return True def run(self, path): if not self._method: raise AssertionError("Method not found in real specification") if self._unsupported: return # Can't check it. Happens with builtin functions. :-( action = path.actions[-1] obtained_len = len(action.args) obtained_kwargs = action.kwargs.copy() nodefaults_len = len(self._args) - len(self._defaults) for i, name in enumerate(self._args): if i < obtained_len and name in action.kwargs: self._raise("%r provided twice" % name) if (i >= obtained_len and i < nodefaults_len and name not in action.kwargs): self._raise("%r not provided" % name) obtained_kwargs.pop(name, None) if obtained_len > len(self._args) and not self._varargs: self._raise("too many args provided") if obtained_kwargs and not self._varkwargs: self._raise("unknown kwargs: %s" % ", ".join(obtained_kwargs)) def spec_checker_recorder(mocker, event): spec = event.path.root_mock.__mocker_spec__ if spec: actions = event.path.actions if len(actions) == 1: if actions[0].kind == "call": method = getattr(spec, "__call__", None) event.add_task(SpecChecker(method)) elif len(actions) == 2: if actions[0].kind == "getattr" and actions[1].kind == "call": method = getattr(spec, actions[0].args[0], None) event.add_task(SpecChecker(method)) Mocker.add_recorder(spec_checker_recorder) class ProxyReplacer(Task): """Task which installs and deinstalls proxy mocks. This task will replace a real object by a mock in all dictionaries found in the running interpreter via the garbage collecting system. """ def __init__(self, mock): self.mock = mock self.__mocker_replace__ = False def replay(self): global_replace(self.mock.__mocker_object__, self.mock) def restore(self): global_replace(self.mock, self.mock.__mocker_object__) def global_replace(remove, install): """Replace object 'remove' with object 'install' on all dictionaries.""" for referrer in gc.get_referrers(remove): if (type(referrer) is dict and referrer.get("__mocker_replace__", True)): for key, value in list(referrer.iteritems()): if value is remove: referrer[key] = install class Undefined(object): def __repr__(self): return "Undefined" Undefined = Undefined() class Patcher(Task): def __init__(self): super(Patcher, self).__init__() self._monitored = {} # {kind: {id(object): object}} self._patched = {} def is_monitoring(self, obj, kind): monitored = self._monitored.get(kind) if monitored: if id(obj) in monitored: return True cls = type(obj) if issubclass(cls, type): cls = obj bases = set([id(base) for base in cls.__mro__]) bases.intersection_update(monitored) return bool(bases) return False def monitor(self, obj, kind): if kind not in self._monitored: self._monitored[kind] = {} self._monitored[kind][id(obj)] = obj def patch_attr(self, obj, attr, value): original = obj.__dict__.get(attr, Undefined) self._patched[id(obj), attr] = obj, attr, original setattr(obj, attr, value) def get_unpatched_attr(self, obj, attr): cls = type(obj) if issubclass(cls, type): cls = obj result = Undefined for mro_cls in cls.__mro__: key = (id(mro_cls), attr) if key in self._patched: result = self._patched[key][2] if result is not Undefined: break elif attr in mro_cls.__dict__: result = mro_cls.__dict__.get(attr, Undefined) break if isinstance(result, object) and hasattr(type(result), "__get__"): if cls is obj: obj = None return result.__get__(obj, cls) return result def _get_kind_attr(self, kind): if kind == "getattr": return "__getattribute__" return "__%s__" % kind def replay(self): for kind in self._monitored: attr = self._get_kind_attr(kind) seen = set() for obj in self._monitored[kind].itervalues(): cls = type(obj) if issubclass(cls, type): cls = obj if cls not in seen: seen.add(cls) unpatched = getattr(cls, attr, Undefined) self.patch_attr(cls, attr, PatchedMethod(kind, unpatched, self.is_monitoring)) self.patch_attr(cls, "__mocker_execute__", self.execute) def restore(self): for obj, attr, original in self._patched.itervalues(): if original is Undefined: delattr(obj, attr) else: setattr(obj, attr, original) self._patched.clear() def execute(self, action, object): attr = self._get_kind_attr(action.kind) unpatched = self.get_unpatched_attr(object, attr) try: return unpatched(*action.args, **action.kwargs) except AttributeError: type, value, traceback = sys.exc_info() if action.kind == "getattr": # The normal behavior of Python is to try __getattribute__, # and if it raises AttributeError, try __getattr__. We've # tried the unpatched __getattribute__ above, and we'll now # try __getattr__. try: __getattr__ = unpatched("__getattr__") except AttributeError: pass else: return __getattr__(*action.args, **action.kwargs) raise type, value, traceback class PatchedMethod(object): def __init__(self, kind, unpatched, is_monitoring): self._kind = kind self._unpatched = unpatched self._is_monitoring = is_monitoring def __get__(self, obj, cls=None): object = obj or cls if not self._is_monitoring(object, self._kind): return self._unpatched.__get__(obj, cls) def method(*args, **kwargs): if self._kind == "getattr" and args[0].startswith("__mocker_"): return self._unpatched.__get__(obj, cls)(args[0]) mock = object.__mocker_mock__ return mock.__mocker_act__(self._kind, args, kwargs, object) return method def __call__(self, obj, *args, **kwargs): # At least with __getattribute__, Python seems to use *both* the # descriptor API and also call the class attribute directly. It # looks like an interpreter bug, or at least an undocumented # inconsistency. return self.__get__(obj)(*args, **kwargs) def patcher_recorder(mocker, event): mock = event.path.root_mock if mock.__mocker_patcher__ and len(event.path.actions) == 1: patcher = mock.__mocker_patcher__ patcher.monitor(mock.__mocker_object__, event.path.actions[0].kind) Mocker.add_recorder(patcher_recorder) mocker-1.0/setup.cfg0000644000175000017500000000015711407524705014677 0ustar niemeyerniemeyer[bdist_rpm] use_bzip2 = 1 [sdist] formats = bztar [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 mocker-1.0/setup.py0000755000175000017500000000201211407520312014551 0ustar niemeyerniemeyer#!/usr/bin/env python import os import re try: from setuptools import setup, Extension except ImportError: from distutils.core import setup, Extension if os.path.isfile("MANIFEST"): os.unlink("MANIFEST") version = re.search('__version__ = "([^"]+)"', open("mocker.py").read()).group(1) setup( name="mocker", version=version, description="Graceful platform for test doubles in Python (mocks, stubs, fakes, and dummies).", author="Gustavo Niemeyer", author_email="gustavo@niemeyer.net", license="BSD", url="http://labix.org/mocker", download_url="https://launchpad.net/mocker/+download", py_modules=["mocker"], classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Python Software Foundation License", "Programming Language :: Python", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Libraries :: Python Modules", ], ) mocker-1.0/test.py0000755000175000017500000044066711407514536014431 0ustar niemeyerniemeyer#!/usr/bin/python import unittest import tempfile import inspect import shutil import sys import os import gc from types import ModuleType try: import coverage except ImportError: coverage = None else: # Start coverage check before importing from mocker, to get all of it. coverage.erase() coverage.start() from mocker import \ MockerBase, Mocker, Mock, Event, Task, Action, Path, recorder, expect, \ PathMatcher, path_matcher_recorder, RunCounter, ImplicitRunCounter, \ run_counter_recorder, run_counter_removal_recorder, MockReturner, \ mock_returner_recorder, FunctionRunner, Orderer, SpecChecker, \ spec_checker_recorder, match_params, ANY, IS, CONTAINS, IN, MATCH, ARGS, \ KWARGS, MatchError, PathExecuter, ProxyReplacer, Patcher, Undefined, \ PatchedMethod, MockerTestCase, ReplayRestoreEvent, OnRestoreCaller, Expect class TestCase(unittest.TestCase): """Python 2.3 lacked a couple of useful aliases.""" assertTrue = unittest.TestCase.failUnless assertFalse = unittest.TestCase.failIf class CleanMocker(MockerBase): """Just a better name for MockerBase in a testing context.""" class IntegrationTest(TestCase): def setUp(self): self.mocker = Mocker() def tearDown(self): self.mocker.restore() def test_count(self): obj = self.mocker.mock() obj.x self.mocker.count(2, 3) self.mocker.replay() obj.x self.assertRaises(AssertionError, self.mocker.verify) obj.x self.mocker.verify() obj.x self.mocker.verify() self.assertRaises(AssertionError, getattr, obj, "x") def test_order(self): obj = self.mocker.mock() with_manager = self.mocker.order() with_manager.__enter__() obj.x obj.y obj.z with_manager.__exit__(None, None, None) self.mocker.replay() self.assertRaises(AssertionError, getattr, obj, "y") self.assertRaises(AssertionError, getattr, obj, "z") self.mocker.replay() obj.x self.assertRaises(AssertionError, getattr, obj, "z") self.mocker.replay() obj.x obj.y obj.z def test_spec_and_type(self): class C(object): def m(self, a): pass obj = self.mocker.mock(C) obj.m(1) obj.m(a=1) obj.m(1, 2) obj.m(b=2) obj.x() obj.y() self.mocker.nospec() obj.z() self.mocker.replay() self.assertTrue(isinstance(obj, C)) obj.m(1) obj.m(a=1) obj.y() self.assertRaises(AssertionError, obj.m, 1, 2) self.assertRaises(AssertionError, obj.m, b=2) self.assertRaises(AssertionError, obj.x) self.assertRaises(AssertionError, obj.z) def test_result(self): obj = self.mocker.mock() obj.x self.mocker.result(42) self.mocker.replay() self.assertEquals(obj.x, 42) def test_throw(self): obj = self.mocker.mock() obj.x() self.mocker.throw(ValueError) self.mocker.replay() self.assertRaises(ValueError, obj.x) def test_call(self): calls = [] def func(arg): calls.append(arg) return 42 obj = self.mocker.mock() obj.x(24) self.mocker.call(func) self.mocker.replay() self.assertEquals(obj.x(24), 42) self.assertEquals(calls, [24]) def test_call_result(self): calls = [] def func(arg): calls.append(arg) return arg obj = self.mocker.mock() obj.x(24) self.mocker.call(func) self.mocker.result(42) self.mocker.replay() self.assertEquals(obj.x(24), 42) self.assertEquals(calls, [24]) def test_generate(self): obj = self.mocker.mock() obj.x(24) self.mocker.generate([1, 2, 3]) self.mocker.replay() result = obj.x(24) def g(): yield None self.assertEquals(type(result), type(g())) self.assertEquals(list(result), [1, 2, 3]) def test_proxy(self): class C(object): def sum(self, *args): return sum(args) obj = self.mocker.proxy(C()) expect(obj.multiply(2, 3)).result(6).nospec() expect(obj.sum(0, 0)).result(1) expect(obj.sum(0, 0)).passthrough() self.mocker.replay() self.assertEquals(obj.multiply(2, 3), 6) # Mocked. self.assertRaises(AttributeError, obj.multiply) # Passed through. self.assertEquals(obj.sum(2, 3), 5) # Passed through. self.assertEquals(obj.sum(0, 0), 1) # Mocked. self.assertEquals(obj.sum(0, 0), 0) # Passed through explicitly. self.assertRaises(AssertionError, obj.sum, 0, 0) # Seen twice. def test_replace_install_and_restore(self): module = self.mocker.replace("calendar") import calendar self.assertTrue(calendar is not module) self.mocker.replay() import calendar self.assertTrue(calendar is module) self.mocker.restore() import calendar self.assertTrue(calendar is not module) def test_replace_os_path_join(self): path = self.mocker.replace("os.path") expect(path.join(ARGS)).call(lambda *args: "-".join(args)) expect(path.join("e", ARGS)).passthrough() self.mocker.replay() import os self.assertEquals(os.path.join("a", "b", "c"), "a-b-c") self.assertNotEquals(os.path.join("e", "f", "g"), "e-f-g") def test_replace_os_path_isfile(self): path = self.mocker.replace("os.path") expect(path.isfile("unexistent")).result(True) expect(path.isfile(ANY)).passthrough().count(2) self.mocker.replay() import os self.assertFalse(os.path.isfile("another-unexistent")) self.assertTrue(os.path.isfile("unexistent")) self.assertFalse(os.path.isfile("unexistent")) def test_replace_class_method(self): empty = self.mocker.replace("Queue.Queue.empty") expect(empty()).result(False) self.mocker.replay() from Queue import Queue self.assertEquals(Queue().empty(), False) def test_patch_with_spec(self): class C(object): def method(self, a, b): pass mock = self.mocker.patch(C) mock.method(1, 2) mock.method(1) self.mocker.replay() mock.method(1, 2) self.assertRaises(AssertionError, mock.method, 1) def test_patch_with_spec_and_unexistent(self): class C(object): pass mock = self.mocker.patch(C) mock.method(1, 2) self.mocker.count(0) self.mocker.replay() self.assertRaises(AssertionError, self.mocker.verify) def test_mock_iter(self): """ list() uses len() as a hint. When we mock iter(), it shouldn't explode due to the lack of len(). """ mock = self.mocker.mock() iter(mock) self.mocker.result(iter([1, 2, 3])) self.mocker.replay() self.assertEquals(list(mock), [1, 2, 3]) self.mocker.verify() def test_replace_builtin_function(self): """ Inspection doesn't work on builtin functions, but proxying should work even then (without spec enforcement in these cases). """ from zlib import adler32 mock = self.mocker.proxy(adler32) mock() self.mocker.result(42) self.mocker.replay() self.assertEquals(mock(), 42) class ExpectTest(TestCase): def setUp(self): self.mocker = CleanMocker() def test_calling_mocker(self): obj = self.mocker.mock() expect(obj.attr).result(123) self.mocker.replay() self.assertEquals(obj.attr, 123) def test_chaining(self): obj = self.mocker.mock() expect(obj.attr).result(123).result(42) self.mocker.replay() self.assertEquals(obj.attr, 42) def test_explicit_expect_instance(self): obj = self.mocker.mock() myexpect = Expect(self.mocker) myexpect(iter(obj)).generate([1, 2, 3]).count(1, 2) self.mocker.replay() self.assertEquals(list(obj), [1, 2, 3]) class MockerTestCaseTest(TestCase): def setUp(self): self.test = MockerTestCase("shortDescription") def tearDown(self): self.test.mocker.restore() # Run it so that any cleanups are performed. self.test.run() def test_has_mocker(self): self.assertEquals(type(self.test.mocker), Mocker) def test_has_expect(self): self.assertTrue(issubclass(self.test.expect, expect)) def test_expect_works_with_non_mocks(self): # We must be using the Expect helper for this to work at all: obj = self.test.mocker.mock() self.test.expect(iter(obj)).generate([1,2,3]) def test_attributes_are_the_same(self): class MyTest(MockerTestCase): def test_method(self): pass test_method.foo = "bar" test = MyTest("test_method") self.assertEquals(getattr(test.test_method, "im_class", None), MyTest) self.assertEquals(getattr(test.test_method, "foo", None), "bar") def test_constructor_is_the_same(self): self.assertEquals(inspect.getargspec(TestCase.__init__), inspect.getargspec(MockerTestCase.__init__)) def test_docstring_is_the_same(self): class MyTest(MockerTestCase): def test_method(self): """Hello there!""" self.assertEquals(MyTest("test_method").test_method.__doc__, "Hello there!") def test_short_description_is_the_same(self): class MyTest(MockerTestCase): def test_method(self): """Hello there!""" class StandardTest(TestCase): def test_method(self): """Hello there!""" self.assertEquals(MyTest("test_method").shortDescription(), StandardTest("test_method").shortDescription()) def test_missing_method_raises_the_same_error(self): class MyTest(TestCase): pass try: MyTest("unexistent_method").run() except Exception, e: expected_error = e class MyTest(MockerTestCase): pass try: MyTest("unexistent_method").run() except Exception, e: self.assertEquals(str(e), str(expected_error)) self.assertEquals(type(e), type(expected_error)) def test_raises_runtime_error_if_not_in_replay_mode_with_events(self): class MyTest(MockerTestCase): def test_method(self): pass test = MyTest("test_method") # That's fine. test.test_method() test.mocker.add_event(Event()) # That's not. self.assertRaises(RuntimeError, test.test_method) test.mocker.replay() # Fine again. test.test_method() def test_mocker_is_verified_and_restored_after_test_method_is_run(self): calls = [] class MyEvent(Event): def verify(self): calls.append("verify") def restore(self): calls.append("restore") class MyTest(MockerTestCase): def test_method(self): self.mocker.add_event(MyEvent()) self.mocker.replay() def test_method_raising(self): self.mocker.add_event(MyEvent()) self.mocker.replay() raise AssertionError("BOOM!") result = unittest.TestResult() MyTest("test_method").run(result) self.assertEquals(calls, ["verify", "restore"]) self.assertTrue(result.wasSuccessful()) del calls[:] result = unittest.TestResult() MyTest("test_method_raising").run(result) self.assertEquals(calls, ["restore"]) self.assertEquals(len(result.errors), 0) self.assertEquals(len(result.failures), 1) self.assertTrue("BOOM!" in result.failures[0][1]) del calls[:] result = unittest.TestResult() # Running twice in the same instance (Trial does that). test = MyTest("test_method") test.run(result) test.run(result) self.assertEquals(calls, ["verify", "restore", "verify", "restore"]) self.assertTrue(result.wasSuccessful()) def test_expectation_failure_acts_appropriately(self): class MyTest(MockerTestCase): def test_method(self): mock = self.mocker.mock() mock.x self.mocker.replay() result = unittest.TestResult() MyTest("test_method").run(result) self.assertEquals(len(result.errors), 0) self.assertEquals(len(result.failures), 1) self.assertTrue("mock.x" in result.failures[0][1]) def test_add_cleanup(self): stash = [] def func(a, b): stash.append((a, b)) class MyTest(MockerTestCase): def tearDown(self): self.addCleanup(func, 3, b=4) def test_method(self): self.addCleanup(func, 1, b=2) stash.append(stash[:]) MyTest("test_method").run() self.assertEquals(stash, [[], (1, 2), (3, 4)]) def test_cleanup_wrapper_in__call__for_2_3(self): version_info = sys.version_info __call__ = unittest.TestCase.__call__ try: sys.version_info = (2, 3, 5) stash = [] def call(self, *args, **kwargs): self.addCleanup(lambda: stash.append(True)) unittest.TestCase.__call__ = call class MyTest(MockerTestCase): def test_method(self): pass MyTest("test_method")() self.assertEquals(stash, [True]) finally: unittest.TestCase.__call__ = __call__ sys.version_info = version_info def test_cleanup_wrapper_in__call__for_2_4(self): version_info = sys.version_info __call__ = unittest.TestCase.__call__ try: sys.version_info = (2, 4) stash = [] def call(self, *args, **kwargs): self.addCleanup(lambda: stash.append(True)) unittest.TestCase.__call__ = call class MyTest(MockerTestCase): def test_method(self): pass MyTest("test_method")() # Python 2.4+ handles cleanup in run(), registered inside # MockerTestCase.__init__, so this should *not* work. self.assertEquals(stash, []) finally: unittest.TestCase.__call__ = __call__ sys.version_info = version_info def test_twisted_trial_deferred_support(self): calls = [] callbacks = [] errbacks = [] deferreds = [] class Deferred(object): def addCallback(self, callback): callbacks.append(callback) def addErrback(self, errback): errbacks.append(errback) class MyEvent(Event): def verify(self): calls.append("verify") def restore(self): calls.append("restore") class MyTest(MockerTestCase): def test_method(self): self.mocker.add_event(MyEvent()) self.mocker.replay() deferred = Deferred() deferreds.append(deferred) return deferred result = unittest.TestResult() test = MyTest("test_method") deferred = test.test_method() self.assertEquals(deferred, deferreds[-1]) self.assertEquals(calls, []) self.assertEquals(len(callbacks), 1) self.assertEquals(callbacks[-1]("foo"), "foo") self.assertEquals(calls, ["verify"]) def test_fail_unless_is_raises_on_mismatch(self): try: self.test.failUnlessIs([], []) except AssertionError, e: self.assertEquals(str(e), "[] is not []") else: self.fail("AssertionError not raised") def test_fail_unless_is_uses_msg(self): try: self.test.failUnlessIs([], [], "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_unless_is_succeeds(self): obj = [] try: self.test.failUnlessIs(obj, obj) except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_if_is_raises_on_mismatch(self): obj = [] try: self.test.failIfIs(obj, obj) except AssertionError, e: self.assertEquals(str(e), "[] is []") else: self.fail("AssertionError not raised") def test_fail_if_is_uses_msg(self): obj = [] try: self.test.failIfIs(obj, obj, "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_if_is_succeeds(self): try: self.test.failIfIs([], []) except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_unless_in_raises_on_mismatch(self): try: self.test.failUnlessIn(1, []) except AssertionError, e: self.assertEquals(str(e), "1 not in []") else: self.fail("AssertionError not raised") def test_fail_unless_in_uses_msg(self): try: self.test.failUnlessIn(1, [], "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_unless_in_succeeds(self): try: self.test.failUnlessIn(1, [1]) except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_if_in_raises_on_mismatch(self): try: self.test.failIfIn(1, [1]) except AssertionError, e: self.assertEquals(str(e), "1 in [1]") else: self.fail("AssertionError not raised") def test_fail_if_in_uses_msg(self): try: self.test.failIfIn(1, [1], "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_if_in_succeeds(self): try: self.test.failIfIn(1, []) except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_unless_starts_with_raises_on_mismatch(self): try: self.test.failUnlessStartsWith("abc", "def") except AssertionError, e: self.assertEquals(str(e), "'abc' doesn't start with 'def'") else: self.fail("AssertionError not raised") def test_fail_unless_starts_with_uses_msg(self): try: self.test.failUnlessStartsWith("abc", "def", "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_unless_starts_with_succeeds(self): try: self.test.failUnlessStartsWith("abcdef", "abc") except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_unless_starts_with_works_with_non_strings(self): self.test.failUnlessStartsWith([1, 2, 3], [1, 2]) self.assertRaises(AssertionError, self.test.failUnlessStartsWith, [1, 2, 3], [4, 5, 6]) def test_fail_if_starts_with_raises_on_mismatch(self): try: self.test.failIfStartsWith("abcdef", "abc") except AssertionError, e: self.assertEquals(str(e), "'abcdef' starts with 'abc'") else: self.fail("AssertionError not raised") def test_fail_if_starts_with_uses_msg(self): try: self.test.failIfStartsWith("abcdef", "abc", "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_if_starts_with_succeeds(self): try: self.test.failIfStartsWith("abc", "def") except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_if_starts_with_works_with_non_strings(self): self.test.failIfStartsWith([1, 2, 3], [4, 5, 6]) self.assertRaises(AssertionError, self.test.failIfStartsWith, [1, 2, 3], [1, 2]) def test_fail_unless_ends_with_raises_on_mismatch(self): try: self.test.failUnlessEndsWith("abc", "def") except AssertionError, e: self.assertEquals(str(e), "'abc' doesn't end with 'def'") else: self.fail("AssertionError not raised") def test_fail_unless_ends_with_uses_msg(self): try: self.test.failUnlessEndsWith("abc", "def", "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_unless_ends_with_succeeds(self): try: self.test.failUnlessEndsWith("abcdef", "def") except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_unless_ends_with_works_with_non_strings(self): self.test.failUnlessEndsWith([1, 2, 3], [2, 3]) self.assertRaises(AssertionError, self.test.failUnlessEndsWith, [1, 2, 3], [4, 5, 6]) def test_fail_if_ends_with_raises_on_mismatch(self): try: self.test.failIfEndsWith("abcdef", "def") except AssertionError, e: self.assertEquals(str(e), "'abcdef' ends with 'def'") else: self.fail("AssertionError not raised") def test_fail_if_ends_with_uses_msg(self): try: self.test.failIfEndsWith("abcdef", "def", "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_if_ends_with_succeeds(self): try: self.test.failIfEndsWith("abc", "def") except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_if_ends_with_works_with_non_strings(self): self.test.failIfEndsWith([1, 2, 3], [4, 5, 6]) self.assertRaises(AssertionError, self.test.failIfEndsWith, [1, 2, 3], [2, 3]) def test_fail_unless_approximates_raises_on_mismatch(self): try: self.test.failUnlessApproximates(1, 2, 0.999) except AssertionError, e: self.assertEquals(str(e), "abs(1 - 2) > 0.999") else: self.fail("AssertionError not raised") def test_fail_unless_approximates_uses_msg(self): try: self.test.failUnlessApproximates(1, 2, 0.999, "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_unless_approximates_succeeds(self): try: self.test.failUnlessApproximates(1, 2, 1) except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_if_approximates_raises_on_mismatch(self): try: self.test.failIfApproximates(1, 2, 1) except AssertionError, e: self.assertEquals(str(e), "abs(1 - 2) <= 1") else: self.fail("AssertionError not raised") def test_fail_if_approximates_uses_msg(self): try: self.test.failIfApproximates(1, 2, 1, "oops!") except AssertionError, e: self.assertEquals(str(e), "oops!") else: self.fail("AssertionError not raised") def test_fail_if_approximates_succeeds(self): try: self.test.failIfApproximates(1, 2, 0.999) except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_unless_methods_match_raises_on_different_method(self): class Fake(object): def method(self, a): pass class Real(object): def method(self, b): pass try: self.test.failUnlessMethodsMatch(Fake, Real) except AssertionError, e: self.assertEquals(str(e), "Fake.method(self, a) != " "Real.method(self, b)") else: self.fail("AssertionError not raised") def test_fail_unless_methods_match_raises_on_missing_method(self): class Fake(object): def method(self, a): pass class Real(object): pass try: self.test.failUnlessMethodsMatch(Fake, Real) except AssertionError, e: self.assertEquals(str(e), "Fake.method(self, a) not present " "in Real") else: self.fail("AssertionError not raised") def test_fail_unless_methods_match_succeeds_on_missing_priv_method(self): class Fake(object): def _method(self, a): pass class Real(object): pass try: self.test.failUnlessMethodsMatch(Fake, Real) except AssertionError, e: self.fail("AssertionError shouldn't be raised") def test_fail_unless_methods_match_raises_on_different_priv_method(self): class Fake(object): def _method(self, a): pass class Real(object): def _method(self, b): pass try: self.test.failUnlessMethodsMatch(Fake, Real) except AssertionError, e: self.assertEquals(str(e), "Fake._method(self, a) != " "Real._method(self, b)") else: self.fail("AssertionError not raised") def test_fail_unless_methods_match_succeeds(self): class Fake(object): def method(self, a): pass class Real(object): def method(self, a): pass obj = [] try: self.test.failUnlessMethodsMatch(Fake, Real) except AssertionError: self.fail("AssertionError shouldn't be raised") def test_fail_unless_raises_succeeds(self): class MyException(Exception): pass def f(*args): raise MyException(*args) error = self.test.failUnlessRaises(MyException, f, 1, "foo") self.assertEquals(error.args, (1, "foo")) def test_fail_unless_raises_error(self): def f(*args): return args try: self.test.failUnlessRaises(ValueError, f, 1, "foo") except AssertionError, e: self.assertEquals( str(e), "ValueError not raised ((1, 'foo') returned)") else: self.fail("AssertionError not raised") def test_fail_unless_raises_other_exception(self): class MyException1(Exception): pass class MyException2(Exception): pass def f(*args): raise MyException2(*args) try: self.test.failUnlessRaises(MyException1, f, 1, "foo") except MyException2: pass else: self.fail("MyException2 not raised") def test_aliases(self): get_method = MockerTestCase.__dict__.get self.assertEquals(get_method("assertIs"), get_method("failUnlessIs")) self.assertEquals(get_method("assertIsNot"), get_method("failIfIs")) self.assertEquals(get_method("assertIn"), get_method("failUnlessIn")) self.assertEquals(get_method("assertNotIn"), get_method("failIfIn")) self.assertEquals(get_method("assertStartsWith"), get_method("failUnlessStartsWith")) self.assertEquals(get_method("assertNotStartsWith"), get_method("failIfStartsWith")) self.assertEquals(get_method("assertEndsWith"), get_method("failUnlessEndsWith")) self.assertEquals(get_method("assertNotEndsWith"), get_method("failIfEndsWith")) self.assertEquals(get_method("assertApproximates"), get_method("failUnlessApproximates")) self.assertEquals(get_method("assertNotApproximates"), get_method("failIfApproximates")) self.assertEquals(get_method("assertMethodsMatch"), get_method("failUnlessMethodsMatch")) self.assertEquals(get_method("assertRaises"), get_method("failUnlessRaises")) def test_twisted_trial_aliases(self): get_method = MockerTestCase.__dict__.get self.assertEquals(get_method("assertIdentical"), get_method("assertIs")) self.assertEquals(get_method("assertNotIdentical"), get_method("assertIsNot")) self.assertEquals(get_method("failUnlessIdentical"), get_method("failUnlessIs")) self.assertEquals(get_method("failIfIdentical"), get_method("failIfIs")) def test_missing_python23_aliases(self): self.assertEquals(MockerTestCase.assertTrue.im_func, MockerTestCase.failUnless.im_func) self.assertEquals(MockerTestCase.assertFalse.im_func, MockerTestCase.failIf.im_func) def test_make_file_returns_writable_filename(self): filename = self.test.makeFile() self.assertFalse(os.path.isfile(filename)) open(filename, "w").write("Is writable!") def test_make_file_creates_file(self): filename = self.test.makeFile("") self.assertEquals(os.path.getsize(filename), 0) def test_make_file_cleansup_on_success(self): filename = self.test.makeFile() self.test.run() self.assertEquals(os.path.isfile(filename), False) def test_make_file_cleansup_on_failure(self): class MyTest(MockerTestCase): def test_method(self): raise AssertionError("BOOM!") test = MyTest("test_method") filename = test.makeFile() test.run() self.assertEquals(os.path.isfile(filename), False) def test_make_file_with_content(self): filename = self.test.makeFile("content") self.assertEquals(open(filename).read(), "content") def test_make_file_with_prefix(self): filename = self.test.makeFile(prefix="prefix-") self.assertTrue(os.path.basename(filename).startswith("prefix-")) def test_make_file_with_suffix(self): filename = self.test.makeFile(suffix="-suffix") self.assertTrue(os.path.basename(filename).endswith("-suffix")) def test_make_file_with_dirname(self): dirname = tempfile.mkdtemp() try: filename = self.test.makeFile(dirname=dirname) self.assertEquals(os.path.dirname(filename), dirname) finally: shutil.rmtree(dirname) def test_make_file_with_basename(self): filename = self.test.makeFile(basename="basename") self.assertEquals(os.path.basename(filename), "basename") self.test.run() self.assertFalse(os.path.exists(filename)) def test_make_file_with_basename_and_dirname(self): dirname = tempfile.mkdtemp() try: filename = self.test.makeFile(dirname=dirname, basename="basename") self.assertEquals(os.path.dirname(filename), dirname) self.assertEquals(os.path.basename(filename), "basename") finally: shutil.rmtree(dirname) def test_make_file_with_path(self): path = tempfile.mktemp() try: filename = self.test.makeFile("", path=path) self.assertEquals(filename, path) self.assertEquals(os.path.getsize(filename), 0) self.test.run() self.assertFalse(os.path.exists(filename)) finally: if os.path.isfile(path): os.unlink(path) def test_make_dir_returns_dirname(self): dirname = self.test.makeDir() self.assertEquals(os.path.isdir(dirname), True) def test_make_dir_cleansup_on_success(self): dirname = self.test.makeDir() self.test.run() self.assertEquals(os.path.isdir(dirname), False) def test_make_dir_cleansup_on_failure(self): class MyTest(MockerTestCase): def test_method(self): raise AssertionError("BOOM!") test = MyTest("test_method") dirname = test.makeDir() test.run() self.assertEquals(os.path.isdir(dirname), False) def test_make_dir_with_prefix(self): dirname = self.test.makeDir(prefix="prefix-") self.assertTrue(os.path.basename(dirname).startswith("prefix-")) def test_make_dir_with_suffix(self): dirname = self.test.makeDir(suffix="-suffix") self.assertTrue(os.path.basename(dirname).endswith("-suffix")) def test_make_dir_with_dirname(self): dirname = tempfile.mkdtemp() try: path = self.test.makeDir(dirname=dirname) self.assertEquals(os.path.dirname(path), dirname) finally: if os.path.exists(dirname): shutil.rmtree(dirname) def test_make_dir_with_path(self): path = tempfile.mktemp() try: self.assertEquals(self.test.makeDir(path=path), path) self.assertEquals(os.path.isdir(path), True) self.test.run() self.assertEquals(os.path.isdir(path), False) finally: if os.path.exists(path): shutil.rmtree(path) def test_mocker_is_restored_between_method_return_and_cleanup(self): """ Certain base classes which MockerTestCase is mixed with may have additional logic after the real test method is run, but before run() returns. We don't want to perform mocking checks on these. """ class CustomTestCase(unittest.TestCase): def run(self, result): import time super(CustomTestCase, self).run(result) self.assertTrue(time.time()) class MyTest(CustomTestCase, MockerTestCase): def test_method(self): import time time_mock = self.mocker.replace(time.time) time_mock() self.mocker.count(0) # Forbit it entirely. self.mocker.replay() result = unittest.TestResult() MyTest("test_method").run(result) self.assertEquals(result.errors, []) self.assertEquals(result.failures, []) class MockerTest(TestCase): def setUp(self): self.recorded = [] self.mocker = CleanMocker() def recorder(mocker, event): self.recorded.append((mocker, event)) self.mocker.add_recorder(recorder) self.action = Action("getattr", ("attr",), {}, Path(Mock(self.mocker, name="mock"))) self.path = self.action.path + self.action def test_default_is_recording(self): self.assertTrue(self.mocker.is_recording()) def test_replay(self): calls = [] event = self.mocker.add_event(Event()) task = event.add_task(Task()) task.replay = lambda: calls.append("replay") task.restore = lambda: calls.append("restore") self.mocker.replay() self.assertFalse(self.mocker.is_recording()) self.assertEquals(calls, ["replay"]) self.mocker.replay() self.assertEquals(calls, ["replay", "restore", "replay"]) def test_restore(self): calls = [] event = self.mocker.add_event(Event()) task = event.add_task(Task()) task.replay = lambda: calls.append("replay") task.restore = lambda: calls.append("restore") self.mocker.replay() self.mocker.restore() self.mocker.restore() self.assertTrue(self.mocker.is_recording()) self.assertEquals(calls, ["replay", "restore"]) def test_reset(self): calls = [] event = self.mocker.add_event(Event()) task = event.add_task(Task()) task.restore = lambda: calls.append("restore") self.mocker.replay() self.mocker.reset() self.mocker.reset() self.assertEquals(calls, ["restore"]) self.assertEquals(self.mocker.get_events(), []) def test_reset_removes_ordering(self): self.mocker.order() self.mocker.reset() self.assertFalse(self.mocker.is_ordering()) def test_verify(self): class MyEvent(object): def __init__(self, id, failed): self.id = id self.failed = failed def verify(self): if self.failed: raise AssertionError("%d failed\n- Line 1\n- Line 2\n" % self.id) self.mocker.add_event(MyEvent(1, True)) self.mocker.add_event(MyEvent(2, False)) self.mocker.add_event(MyEvent(3, True)) try: self.mocker.verify() except AssertionError, e: message = os.linesep.join(["[Mocker] Unmet expectations:", "", "=> 1 failed", " - Line 1", " - Line 2", "", "=> 3 failed", " - Line 1", " - Line 2", ""]) self.assertEquals(str(e), message) else: self.fail("AssertionError not raised") def test_verify_errors_need_good_messages(self): class MyEvent(object): def verify(self): raise AssertionError() self.mocker.add_event(MyEvent()) self.assertRaises(RuntimeError, self.mocker.verify) def test_mocker_as_context_manager(self): calls = [] throw = False class MyEvent(Event): def verify(self): calls.append("verify") if throw: raise AssertionError("Some problem") def replay(self): calls.append("replay") def restore(self): calls.append("restore") event = MyEvent() self.mocker.add_event(event) self.assertEquals(calls, []) mocker = self.mocker.__enter__() self.assertTrue(mocker is self.mocker) self.assertEquals(calls, ["replay"]) # Verify without errors. del calls[:] result = self.mocker.__exit__(None, None, None) self.assertEquals(result, False) self.assertEquals(calls, ["restore", "verify"]) throw = True # Verify raising an error. self.mocker.replay() del calls[:] self.assertRaises(AssertionError, self.mocker.__exit__, None, None, None) self.assertEquals(calls, ["restore", "verify"]) # An exception happened in the 'with' block. Verify won't raise. self.mocker.replay() del calls[:] result = self.mocker.__exit__(AssertionError, None, None) self.assertEquals(result, False) self.assertEquals(calls, ["restore"]) def test_add_recorder_on_instance(self): obj1 = object() obj2 = object() mocker = CleanMocker() self.assertEquals(mocker.add_recorder(obj1), obj1) self.assertEquals(mocker.add_recorder(obj2), obj2) self.assertEquals(mocker.get_recorders(), [obj1, obj2]) mocker = CleanMocker() self.assertEquals(mocker.add_recorder(obj1), obj1) self.assertEquals(mocker.get_recorders(), [obj1]) def test_add_recorder_on_class(self): class MyMocker(CleanMocker): pass obj1 = object() obj2 = object() self.assertEquals(MyMocker.add_recorder(obj1), obj1) self.assertEquals(MyMocker.add_recorder(obj2), obj2) mocker = MyMocker() self.assertEquals(mocker.get_recorders(), [obj1, obj2]) mocker = MyMocker() self.assertEquals(mocker.get_recorders(), [obj1, obj2]) def test_add_recorder_on_subclass(self): class MyMocker1(CleanMocker): pass obj1 = object() MyMocker1.add_recorder(obj1) class MyMocker2(MyMocker1): pass obj2 = object() MyMocker2.add_recorder(obj2) self.assertEquals(MyMocker1.get_recorders(), [obj1]) self.assertEquals(MyMocker2.get_recorders(), [obj1, obj2]) def test_remove_recorder_on_instance(self): obj1 = object() obj2 = object() obj3 = object() class MyMocker(CleanMocker): pass MyMocker.add_recorder(obj1) MyMocker.add_recorder(obj2) MyMocker.add_recorder(obj3) mocker = MyMocker() mocker.remove_recorder(obj2) self.assertEquals(mocker.get_recorders(), [obj1, obj3]) self.assertEquals(MyMocker.get_recorders(), [obj1, obj2, obj3]) def test_remove_recorder_on_class(self): class MyMocker(CleanMocker): pass obj1 = object() obj2 = object() self.assertEquals(MyMocker.add_recorder(obj1), obj1) self.assertEquals(MyMocker.add_recorder(obj2), obj2) MyMocker.remove_recorder(obj1) self.assertEquals(MyMocker.get_recorders(), [obj2]) def test_mock(self): mock = self.mocker.mock() self.assertEquals(mock.__mocker_name__, None) self.assertEquals(mock.__mocker_spec__, None) self.assertEquals(mock.__mocker_type__, None) self.assertEquals(mock.__mocker_count__, True) def test_mock_with_name(self): mock = self.mocker.mock(name="name") self.assertEquals(mock.__mocker_name__, "name") def test_mock_with_spec(self): class C(object): pass mock = self.mocker.mock(spec=C) self.assertEquals(mock.__mocker_spec__, C) def test_mock_with_type(self): class C(object): pass mock = self.mocker.mock(type=C) self.assertEquals(mock.__mocker_type__, C) def test_mock_with_spec_and_type(self): class C(object): pass mock = self.mocker.mock(C) self.assertEquals(mock.__mocker_spec__, C) self.assertEquals(mock.__mocker_type__, C) def test_mock_with_count(self): class C(object): pass mock = self.mocker.mock(count=False) self.assertEquals(mock.__mocker_count__, False) def test_proxy(self): original = object() mock = self.mocker.proxy(original) self.assertEquals(type(mock), Mock) self.assertEquals(mock.__mocker_object__, original) self.assertEquals(mock.__mocker_path__.root_object, original) self.assertEquals(mock.__mocker_count__, True) def test_proxy_with_count(self): original = object() mock = self.mocker.proxy(original, count=False) self.assertEquals(mock.__mocker_count__, False) def test_proxy_with_spec(self): original = object() class C(object): pass mock = self.mocker.proxy(original, C) self.assertEquals(mock.__mocker_object__, original) self.assertEquals(mock.__mocker_spec__, C) def test_proxy_with_type(self): original = object() class C(object): pass mock = self.mocker.proxy(original, type=C) self.assertEquals(mock.__mocker_type__, C) def test_proxy_spec_defaults_to_the_object_itself(self): original = object() mock = self.mocker.proxy(original) self.assertEquals(mock.__mocker_spec__, original) def test_proxy_type_defaults_to_the_object_type(self): original = object() mock = self.mocker.proxy(original) self.assertEquals(mock.__mocker_type__, object) def test_proxy_with_spec_and_type_none(self): original = object() mock = self.mocker.proxy(original, spec=None, type=None) self.assertEquals(mock.__mocker_spec__, None) self.assertEquals(mock.__mocker_type__, None) def test_proxy_with_passthrough_false(self): original = object() class C(object): pass mock = self.mocker.proxy(original, C, passthrough=False) self.assertEquals(mock.__mocker_object__, original) self.assertEquals(mock.__mocker_spec__, C) self.assertEquals(mock.__mocker_passthrough__, False) def test_proxy_with_submodule_string(self): from os import path module = self.mocker.proxy("os.path") self.assertEquals(type(module), Mock) self.assertEquals(type(module.__mocker_object__), ModuleType) self.assertEquals(module.__mocker_name__, "os.path") self.assertEquals(module.__mocker_object__, path) def test_proxy_with_module_function_string(self): mock = self.mocker.proxy("os.path.join.func_name") self.assertEquals(mock.__mocker_object__, "join") def test_proxy_with_string_and_name(self): module = self.mocker.proxy("os.path", name="mock") self.assertEquals(module.__mocker_name__, "mock") def test_proxy_with_unexistent_module(self): self.assertRaises(ImportError, self.mocker.proxy, "unexistent.module") def test_replace(self): from os import path obj = object() proxy = self.mocker.replace(obj, spec=object, name="obj", count=False, passthrough=False) self.assertEquals(type(proxy), Mock) self.assertEquals(type(proxy.__mocker_object__), object) self.assertEquals(proxy.__mocker_object__, obj) self.assertEquals(proxy.__mocker_spec__, object) self.assertEquals(proxy.__mocker_name__, "obj") self.assertEquals(proxy.__mocker_count__, False) (event,) = self.mocker.get_events() self.assertEquals(type(event), ReplayRestoreEvent) (task,) = event.get_tasks() self.assertEquals(type(task), ProxyReplacer) self.assertTrue(task.mock is proxy) self.assertTrue(task.mock.__mocker_object__ is obj) self.assertTrue(proxy is not obj) def test_replace_with_submodule_string(self): from os import path module = self.mocker.replace("os.path") self.assertEquals(type(module), Mock) self.assertEquals(type(module.__mocker_object__), ModuleType) self.assertEquals(module.__mocker_name__, "os.path") self.assertEquals(module.__mocker_object__, path) (event,) = self.mocker.get_events() (task,) = event.get_tasks() self.assertEquals(type(task), ProxyReplacer) self.assertTrue(task.mock is module) self.assertTrue(task.mock.__mocker_object__ is path) self.assertTrue(module is not path) def test_replace_with_module_function_string(self): mock = self.mocker.replace("os.path.join.func_name") self.assertEquals(mock.__mocker_object__, "join") def test_replace_with_string_and_name(self): module = self.mocker.replace("os.path", name="mock") self.assertEquals(module.__mocker_name__, "mock") def test_replace_with_type(self): original = object() class C(object): pass mock = self.mocker.replace(original, type=C) self.assertEquals(mock.__mocker_type__, C) def test_replace_spec_defaults_to_the_object_itself(self): original = object() mock = self.mocker.replace(original) self.assertEquals(mock.__mocker_spec__, original) def test_replace_type_defaults_to_the_object_type(self): original = object() mock = self.mocker.replace(original) self.assertEquals(mock.__mocker_type__, object) def test_replace_with_spec_and_type_none(self): original = object() mock = self.mocker.replace(original, spec=None, type=None) self.assertEquals(mock.__mocker_spec__, None) self.assertEquals(mock.__mocker_type__, None) def test_replace_with_passthrough_false(self): original = object() class C(object): pass mock = self.mocker.replace(original, passthrough=False) self.assertEquals(mock.__mocker_passthrough__, False) def test_replace_with_bound_method(self): from Queue import Queue mock = self.mocker.replace(Queue.empty) self.assertEquals(mock.__mocker_object__, Queue.empty.im_func) def test_add_and_get_event(self): self.mocker.add_event(41) self.assertEquals(self.mocker.add_event(42), 42) self.assertEquals(self.mocker.get_events(), [41, 42]) def test_recording(self): obj = self.mocker.mock() obj.attr() self.assertEquals(len(self.recorded), 2) action1 = Action("getattr", ("attr",), {}) action2 = Action("call", (), {}) mocker1, event1 = self.recorded[0] self.assertEquals(mocker1, self.mocker) self.assertEquals(type(event1), Event) self.assertTrue(event1.path.matches(Path(obj, None, [action1]))) mocker2, event2 = self.recorded[1] self.assertEquals(mocker2, self.mocker) self.assertEquals(type(event2), Event) self.assertTrue(event2.path.matches(Path(obj, None, [action1, action2]))) self.assertEquals(self.mocker.get_events(), [event1, event2]) def test_recording_result_path(self): obj = self.mocker.mock() result = obj.attr() path = Path(obj, None, [Action("getattr", ("attr",), {}), Action("call", (), {})]) self.assertTrue(result.__mocker_path__.matches(path)) def test_replaying_no_events(self): self.mocker.replay() try: self.mocker.act(self.path) except AssertionError, e: pass else: self.fail("AssertionError not raised") self.assertEquals(str(e), "[Mocker] Unexpected expression: mock.attr") def test_replaying_matching(self): calls = [] class MyTask(Task): def matches(_, path): calls.append("matches") self.assertTrue(self.path.matches(path)) return True def run(_, path): calls.append("run") self.assertTrue(self.path.matches(path)) return "result" event = Event() event.add_task(MyTask()) self.mocker.add_event(event) self.mocker.replay() self.assertEquals(self.mocker.act(self.path), "result") self.assertEquals(calls, ["matches", "run"]) def test_replaying_none_matching(self): calls = [] class MyTask(Task): def matches(_, path): self.assertTrue(self.path.matches(path)) calls.append("matches") return False event = Event() event.add_task(MyTask()) self.mocker.add_event(event) self.mocker.replay() self.assertRaises(AssertionError, self.mocker.act, self.path) self.assertEquals(calls, ["matches"]) def test_replay_order(self): """ When playing back, the precedence of events is as follows: 1. Events with may_run() true 2. Events with satisfied() false 3. Events with has_run() false """ class MyTaskBase(Task): postpone = 2 def may_run(self, path): if not self.postpone: return True self.postpone -= 1 def run(self, path): return self.__class__.__name__ class MyTask1(MyTaskBase): pass class MyTask2(MyTaskBase): pass class MyTask3(MyTaskBase): raised = False def verify(self): if not self.postpone and not self.raised: self.raised = True raise AssertionError("An error") class MyTask4(MyTaskBase): postpone = 0 class MyTask5(MyTaskBase): postpone = 1 event1 = self.mocker.add_event(Event()) event1.add_task(MyTask1()) event2 = self.mocker.add_event(Event()) event2.add_task(MyTask2()) event3 = self.mocker.add_event(Event()) event3.add_task(MyTask3()) event4 = self.mocker.add_event(Event()) event4.add_task(MyTask4()) event5 = self.mocker.add_event(Event()) event5.add_task(MyTask5()) self.mocker.replay() # Labels: [M]ay run, [S]atisfied, [H]as run # State: 1=S 2=S 3= 4=MS 5=S self.assertEquals(self.mocker.act(self.path), "MyTask4") # State: 1=S 2=S 3= 4=MSH 5=S self.assertEquals(self.mocker.act(self.path), "MyTask4") # State: 1=MS 2=MS 3=M 4=MSH 5=MS self.assertEquals(self.mocker.act(self.path), "MyTask3") # State: 1=MS 2=MS 3=MSH 4=MSH 5=MS self.assertEquals(self.mocker.act(self.path), "MyTask1") # State: 1=MSH 2=MS 3=MSH 4=MSH 5=MS self.assertEquals(self.mocker.act(self.path), "MyTask2") # State: 1=MSH 2=MSH 3=MSH 4=MSH 5=MS self.assertEquals(self.mocker.act(self.path), "MyTask5") # State: 1=MSH 2=MSH 3=MSH 4=MSH 5=MSH self.assertEquals(self.mocker.act(self.path), "MyTask1") def test_recorder_decorator(self): result = recorder(42) try: self.assertEquals(result, 42) self.assertEquals(Mocker.get_recorders()[-1], 42) self.assertEquals(MockerBase.get_recorders(), []) finally: Mocker.remove_recorder(42) def test_result(self): event1 = self.mocker.add_event(Event()) event2 = self.mocker.add_event(Event()) self.mocker.result(123) self.assertEquals(event2.run(self.path), 123) def test_throw(self): class MyException(Exception): pass event1 = self.mocker.add_event(Event()) event2 = self.mocker.add_event(Event()) self.mocker.throw(MyException) self.assertRaises(MyException, event2.run, self.path) def test_call(self): event1 = self.mocker.add_event(Event()) event2 = self.mocker.add_event(Event()) self.mocker.call(lambda *args, **kwargs: 123) self.assertEquals(event2.run(self.path), 123) def test_count(self): event1 = self.mocker.add_event(Event()) event2 = self.mocker.add_event(Event()) event2.add_task(ImplicitRunCounter(1)) self.mocker.count(2, 3) self.assertEquals(len(event1.get_tasks()), 0) (task,) = event2.get_tasks() self.assertEquals(type(task), RunCounter) self.assertEquals(task.min, 2) self.assertEquals(task.max, 3) self.mocker.count(4) self.assertEquals(len(event1.get_tasks()), 0) (task,) = event2.get_tasks() self.assertEquals(type(task), RunCounter) self.assertEquals(task.min, 4) self.assertEquals(task.max, 4) def test_order(self): mock1 = self.mocker.mock() mock2 = self.mocker.mock() mock3 = self.mocker.mock() mock4 = self.mocker.mock() result1 = mock1.attr1(1) result2 = mock2.attr2(2) result3 = mock3.attr3(3) result4 = mock4.attr4(4) # Try to spoil the logic which decides which task to reuse. other_task = Task() for event in self.mocker.get_events(): event.add_task(other_task) self.mocker.order(result1, result2, result3) self.mocker.order(result1, result4) self.mocker.order(result2, result4) events = self.mocker.get_events() self.assertEquals(len(events), 8) self.assertEquals(events[0].get_tasks(), [other_task]) other_task_, task1 = events[1].get_tasks() self.assertEquals(type(task1), Orderer) self.assertEquals(task1.path, events[1].path) self.assertEquals(task1.get_dependencies(), []) self.assertEquals(other_task_, other_task) self.assertEquals(events[2].get_tasks(), [other_task]) other_task_, task3 = events[3].get_tasks() self.assertEquals(type(task3), Orderer) self.assertEquals(task3.path, events[3].path) self.assertEquals(task3.get_dependencies(), [task1]) self.assertEquals(other_task_, other_task) self.assertEquals(events[4].get_tasks(), [other_task]) other_task_, task5 = events[5].get_tasks() self.assertEquals(type(task5), Orderer) self.assertEquals(task5.path, events[5].path) self.assertEquals(task5.get_dependencies(), [task3]) self.assertEquals(other_task_, other_task) self.assertEquals(events[6].get_tasks(), [other_task]) other_task_, task7 = events[7].get_tasks() self.assertEquals(type(task7), Orderer) self.assertEquals(task7.path, events[7].path) self.assertEquals(task7.get_dependencies(), [task1, task3]) self.assertEquals(other_task_, other_task) def test_after(self): mock1 = self.mocker.mock() mock2 = self.mocker.mock() mock3 = self.mocker.mock() result1 = mock1.attr1(1) result2 = mock2.attr2(2) result3 = mock3.attr3(3) # Try to spoil the logic which decides which task to reuse. other_task = Task() for event in self.mocker.get_events(): event.add_task(other_task) self.mocker.after(result1, result2) events = self.mocker.get_events() self.assertEquals(len(events), 6) self.assertEquals(events[0].get_tasks(), [other_task]) other_task_, task1 = events[1].get_tasks() self.assertEquals(type(task1), Orderer) self.assertEquals(task1.path, events[1].path) self.assertEquals(task1.get_dependencies(), []) self.assertEquals(other_task_, other_task) self.assertEquals(events[2].get_tasks(), [other_task]) other_task_, task3 = events[3].get_tasks() self.assertEquals(type(task3), Orderer) self.assertEquals(task3.path, events[3].path) self.assertEquals(task3.get_dependencies(), []) self.assertEquals(other_task_, other_task) self.assertEquals(events[4].get_tasks(), [other_task]) other_task_, task5 = events[5].get_tasks() self.assertEquals(type(task5), Orderer) self.assertEquals(task5.path, events[5].path) self.assertEquals(task5.get_dependencies(), [task1, task3]) self.assertEquals(other_task_, other_task) def test_before(self): mock1 = self.mocker.mock() mock2 = self.mocker.mock() mock3 = self.mocker.mock() result1 = mock1.attr1(1) result2 = mock2.attr2(2) result3 = mock3.attr3(3) # Try to spoil the logic which decides which task to reuse. other_task = Task() for event in self.mocker.get_events(): event.add_task(other_task) self.mocker.before(result1, result2) events = self.mocker.get_events() self.assertEquals(len(events), 6) self.assertEquals(events[4].get_tasks(), [other_task]) other_task_, task5 = events[5].get_tasks() self.assertEquals(type(task5), Orderer) self.assertEquals(task5.path, events[5].path) self.assertEquals(task5.get_dependencies(), []) self.assertEquals(other_task_, other_task) self.assertEquals(events[0].get_tasks(), [other_task]) other_task_, task1 = events[1].get_tasks() self.assertEquals(type(task1), Orderer) self.assertEquals(task1.path, events[1].path) self.assertEquals(task1.get_dependencies(), [task5]) self.assertEquals(other_task_, other_task) self.assertEquals(events[2].get_tasks(), [other_task]) other_task_, task3 = events[3].get_tasks() self.assertEquals(type(task3), Orderer) self.assertEquals(task3.path, events[3].path) self.assertEquals(task3.get_dependencies(), [task5]) self.assertEquals(other_task_, other_task) def test_default_ordering(self): self.assertEquals(self.mocker.is_ordering(), False) def test_order_without_arguments(self): self.mocker.order() self.assertEquals(self.mocker.is_ordering(), True) def test_order_with_context_manager(self): with_manager = self.mocker.order() self.assertEquals(self.mocker.is_ordering(), True) with_manager.__enter__() self.assertEquals(self.mocker.is_ordering(), True) with_manager.__exit__(None, None, None) self.assertEquals(self.mocker.is_ordering(), False) def test_unorder(self): self.mocker.order() self.mocker.unorder() self.assertEquals(self.mocker.is_ordering(), False) def test_ordered_events(self): mock = self.mocker.mock() # Ensure that the state is correctly reset between # different ordered blocks. self.mocker.order() mock.a self.mocker.unorder() self.mocker.order() mock.x.y.z events = self.mocker.get_events() (task1,) = events[1].get_tasks() (task2,) = events[2].get_tasks() (task3,) = events[3].get_tasks() self.assertEquals(type(task1), Orderer) self.assertEquals(type(task2), Orderer) self.assertEquals(type(task3), Orderer) self.assertEquals(task1.path, events[1].path) self.assertEquals(task2.path, events[2].path) self.assertEquals(task3.path, events[3].path) self.assertEquals(task1.get_dependencies(), []) self.assertEquals(task2.get_dependencies(), [task1]) self.assertEquals(task3.get_dependencies(), [task2]) def test_nospec(self): event1 = self.mocker.add_event(Event()) event2 = self.mocker.add_event(Event()) task1 = event1.add_task(SpecChecker(None)) task2 = event2.add_task(Task()) task3 = event2.add_task(SpecChecker(None)) task4 = event2.add_task(Task()) self.mocker.nospec() self.assertEquals(event1.get_tasks(), [task1]) self.assertEquals(event2.get_tasks(), [task2, task4]) def test_passthrough(self): obj = object() mock = self.mocker.proxy(obj) event1 = self.mocker.add_event(Event(Path(mock, obj))) event2 = self.mocker.add_event(Event(Path(mock, obj))) self.mocker.passthrough() self.assertEquals(event1.get_tasks(), []) (task,) = event2.get_tasks() self.assertEquals(type(task), PathExecuter) def test_passthrough_fails_on_unproxied(self): mock = self.mocker.mock() event1 = self.mocker.add_event(Event(Path(mock))) event2 = self.mocker.add_event(Event(Path(mock))) self.assertRaises(TypeError, self.mocker.passthrough) def test_passthrough(self): obj = object() mock = self.mocker.proxy(obj) event = self.mocker.add_event(Event(Path(mock, obj))) result_callback = object() self.mocker.passthrough(result_callback) (task,) = event.get_tasks() self.assertEquals(task.get_result_callback(), result_callback) def test_on(self): obj = self.mocker.mock() self.mocker.on(obj.attr).result(123) self.mocker.replay() self.assertEquals(obj.attr, 123) def test_patch(self): class C(object): pass mock = self.mocker.patch(C) self.assertEquals(type(C.__mocker_mock__), Mock) self.assertTrue(C.__mocker_mock__ is mock) self.assertTrue(mock.__mocker_object__ is C) self.assertEquals(type(mock.__mocker_patcher__), Patcher) self.assertEquals(mock.__mocker_passthrough__, True) self.assertEquals(mock.__mocker_spec__, C) (event,) = self.mocker.get_events() self.assertEquals(type(event), ReplayRestoreEvent) (task,) = event.get_tasks() self.assertTrue(task is mock.__mocker_patcher__) def test_patch_without_spec(self): class C(object): pass mock = self.mocker.patch(C, spec=None) self.assertEquals(mock.__mocker_spec__, None) def test_patch_and_restore_unsets_mocker_mock(self): class C(object): pass mock = self.mocker.patch(C) self.mocker.replay() self.assertTrue("__mocker_mock__" in C.__dict__) self.mocker.restore() self.assertFalse("__mocker_mock__" in C.__dict__) class ActionTest(TestCase): def setUp(self): self.mock = Mock(None, name="mock") def test_create(self): objects = [object() for i in range(4)] action = Action(*objects) self.assertEquals(action.kind, objects[0]) self.assertEquals(action.args, objects[1]) self.assertEquals(action.kwargs, objects[2]) self.assertEquals(action.path, objects[3]) def test_repr(self): self.assertEquals(repr(Action("kind", "args", "kwargs")), "Action('kind', 'args', 'kwargs')") self.assertEquals(repr(Action("kind", "args", "kwargs", "path")), "Action('kind', 'args', 'kwargs', 'path')") def test_execute_unknown(self): self.assertRaises(RuntimeError, Action("unknown", (), {}).execute, None) def test_execute_getattr(self): class C(object): pass obj = C() obj.attr = C() action = Action("getattr", ("attr",), {}) self.assertEquals(action.execute(obj), obj.attr) def test_execute_setattr(self): class C(object): pass obj = C() action = Action("setattr", ("attr", "value"), {}) action.execute(obj) self.assertEquals(getattr(obj, "attr", None), "value") def test_execute_delattr(self): class C(object): pass obj = C() obj.attr = "value" action = Action("delattr", ("attr",), {}) action.execute(obj) self.assertEquals(getattr(obj, "attr", None), None) def test_execute_call(self): obj = lambda a, b: a+b action = Action("call", (1,), {"b": 2}) self.assertEquals(action.execute(obj), 3) def test_execute_contains(self): obj = ["a"] action = Action("contains", ("a",), {}) self.assertEquals(action.execute(obj), True) action = Action("contains", ("b",), {}) self.assertEquals(action.execute(obj), False) def test_execute_getitem(self): obj = {"a": 1} action = Action("getitem", ("a",), {}) self.assertEquals(action.execute(obj), 1) action = Action("getitem", ("b",), {}) self.assertRaises(KeyError, action.execute, obj) def test_execute_setitem(self): obj = {} action = Action("setitem", ("a", 1), {}) action.execute(obj) self.assertEquals(obj, {"a": 1}) def test_execute_delitem(self): obj = {"a": 1, "b": 2} action = Action("delitem", ("a",), {}) action.execute(obj) self.assertEquals(obj, {"b": 2}) def test_execute_len(self): obj = [1, 2, 3] action = Action("len", (), {}) self.assertEquals(action.execute(obj), 3) def test_execute_nonzero(self): obj = [] action = Action("nonzero", (), {}) self.assertEquals(action.execute(obj), False) obj = [1] action = Action("nonzero", (), {}) self.assertEquals(action.execute(obj), True) def test_execute_iter(self): obj = [1, 2, 3] action = Action("iter", (), {}) result = action.execute(obj) self.assertEquals(type(result), type(iter(obj))) self.assertEquals(list(result), obj) def test_execute_caching(self): values = iter(range(10)) obj = lambda: values.next() action = Action("call", (), {}) self.assertEquals(action.execute(obj), 0) self.assertEquals(action.execute(obj), 0) obj = lambda: values.next() self.assertEquals(action.execute(obj), 1) def test_equals(self): obj1 = object() obj2 = object() self.assertEquals(Action("kind", (), {}, obj1), Action("kind", (), {}, obj2)) self.assertNotEquals(Action("kind", (), {}, obj1), Action("dnik", (), {}, obj2)) self.assertNotEquals(Action("kind", (), {}, obj1), Action("kind", (1,), {}, obj2)) self.assertNotEquals(Action("kind", (), {}, obj1), Action("kind", (), {"a": 1}, obj2)) self.assertNotEquals(Action("kind", (ANY,), {}, obj1), Action("kind", (1,), {}, obj2)) self.assertEquals(Action("kind", (CONTAINS(1),), {}, obj1), Action("kind", (CONTAINS(1),), {}, obj2)) def test_matches(self): obj1 = object() obj2 = object() action1 = Action("kind", (), {}, obj1) action2 = Action("kind", (), {}, obj2) self.assertTrue(action1.matches(action2)) action1 = Action("kind", (), {}, obj1) action2 = Action("dnik", (), {}, obj2) self.assertFalse(action1.matches(action2)) action1 = Action("kind", (), {}, obj1) action2 = Action("kind", (1,), {}, obj2) self.assertFalse(action1.matches(action2)) action1 = Action("kind", (), {}, obj1) action2 = Action("kind", (), {"a": 1}, obj2) self.assertFalse(action1.matches(action2)) action1 = Action("kind", (ARGS,), {}, obj1) action2 = Action("kind", (), {}, obj2) self.assertTrue(action1.matches(action2)) action1 = Action("kind", (ARGS,), {"a": 1}, obj1) action2 = Action("kind", (), {}, obj2) self.assertFalse(action1.matches(action2)) class PathTest(TestCase): def setUp(self): class StubMocker(object): def act(self, path): pass self.mocker = StubMocker() self.mock = Mock(self.mocker, name="obj") self.object = object() def test_create(self): mock = object() path = Path(mock) self.assertEquals(path.root_mock, mock) self.assertEquals(path.root_object, None) self.assertEquals(path.actions, ()) def test_create_with_object(self): mock = object() path = Path(mock, self.object) self.assertEquals(path.root_mock, mock) self.assertEquals(path.root_object, self.object) def test_create_with_actions(self): mock = object() path = Path(mock, self.object, [1,2,3]) self.assertEquals(path.root_mock, mock) self.assertEquals(path.root_object, self.object) self.assertEquals(path.actions, (1,2,3)) def test_add(self): mock = object() path = Path(mock, self.object, [1,2,3]) result = path + 4 self.assertTrue(result is not path) self.assertEquals(result.root_mock, mock) self.assertEquals(result.root_object, self.object) self.assertEquals(result.actions, (1,2,3,4)) def test_parent_path(self): path1 = Path(self.mock) path2 = path1 + Action("getattr", ("attr",), {}, path1) path3 = path2 + Action("getattr", ("attr",), {}, path2) self.assertEquals(path1.parent_path, None) self.assertEquals(path2.parent_path, path1) self.assertEquals(path3.parent_path, path2) def test_equals(self): mock = object() obj = object() obj1 = object() obj2 = object() # Not the *same* mock. path1 = Path([], obj, []) path2 = Path([], obj, []) self.assertNotEquals(path1, path2) # Not the *same* object. path1 = Path(mock, [], []) path2 = Path(mock, [], []) self.assertNotEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (), {}, obj2)]) self.assertEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("dnik", (), {}, obj2)]) self.assertNotEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(object(), obj, [Action("kind", (), {}, obj2)]) self.assertNotEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (1,), {}, obj2)]) self.assertNotEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (), {"a": 1}, obj2)]) self.assertNotEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, []) self.assertNotEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (ANY,), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (1,), {}, obj2)]) self.assertNotEquals(path1, path2) path1 = Path(mock, obj, [Action("kind", (CONTAINS(1),), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (CONTAINS(1),), {}, obj2)]) self.assertEquals(path1, path2) def test_matches(self): obj = object() mock = object() obj1 = object() obj2 = object() # Not the *same* mock. path1 = Path([], obj, []) path2 = Path([], obj, []) self.assertFalse(path1.matches(path2)) path1 = Path(mock, obj1, []) path2 = Path(mock, obj2, []) self.assertTrue(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (), {}, obj2)]) self.assertTrue(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("dnik", (), {}, obj2)]) self.assertFalse(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(object(), [Action("kind", (), {}, obj2)]) self.assertFalse(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (1,), {}, obj2)]) self.assertFalse(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (), {"a": 1}, obj2)]) self.assertFalse(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (), {}, obj1)]) path2 = Path(mock, obj, []) self.assertFalse(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (ARGS,), {}, obj1)]) path2 = Path(mock, obj, [Action("kind", (), {}, obj2)]) self.assertTrue(path1.matches(path2)) path1 = Path(mock, obj, [Action("kind", (ARGS,), {"a": 1}, obj1)]) path2 = Path(mock, obj, [Action("kind", (), {}, obj2)]) self.assertFalse(path1.matches(path2)) def test_str(self): path = Path(self.mock, []) self.assertEquals(str(path), "obj") def test_str_unnamed(self): mock = Mock(self.mocker) path = Path(mock, []) self.assertEquals(str(path), "") def test_str_auto_named(self): named_mock = Mock(self.mocker) named_mock.attr path = Path(named_mock, []) self.assertEquals(str(path), "named_mock") def test_str_getattr(self): path = Path(self.mock, None, [Action("getattr", ("attr",), {})]) self.assertEquals(str(path), "obj.attr") path += Action("getattr", ("x",), {}) self.assertEquals(str(path), "obj.attr.x") def test_str_getattr_call(self): path = Path(self.mock, None, [Action("getattr", ("x",), {}), Action("getattr", ("y",), {}), Action("call", ("z",), {})]) self.assertEquals(str(path), "obj.x.y('z')") def test_str_setattr(self): path = Path(self.mock, None, [Action("setattr", ("attr", "value"), {})]) self.assertEquals(str(path), "obj.attr = 'value'") def test_str_delattr(self): path = Path(self.mock, None, [Action("delattr", ("attr",), {})]) self.assertEquals(str(path), "del obj.attr") def test_str_call(self): path = Path(self.mock, None, [Action("call", (), {})]) self.assertEquals(str(path), "obj()") path = Path(self.mock, None, [Action("call", (1, "2"), {"a": 3, "b": "4"})]) self.assertEquals(str(path), "obj(1, '2', a=3, b='4')") def test_str_contains(self): path = Path(self.mock, None, [Action("contains", ("value",), {})]) self.assertEquals(str(path), "'value' in obj") def test_str_getitem(self): path = Path(self.mock, None, [Action("getitem", ("key",), {})]) self.assertEquals(str(path), "obj['key']") def test_str_setitem(self): path = Path(self.mock, None, [Action("setitem", ("key", "value"), {})]) self.assertEquals(str(path), "obj['key'] = 'value'") def test_str_delitem(self): path = Path(self.mock, None, [Action("delitem", ("key",), {})]) self.assertEquals(str(path), "del obj['key']") def test_str_len(self): path = Path(self.mock, None, [Action("len", (), {})]) self.assertEquals(str(path), "len(obj)") def test_str_nonzero(self): path = Path(self.mock, None, [Action("nonzero", (), {})]) self.assertEquals(str(path), "bool(obj)") def test_str_iter(self): path = Path(self.mock, None, [Action("iter", (), {})]) self.assertEquals(str(path), "iter(obj)") def test_str_raises_on_unknown(self): path = Path(self.mock, None, [Action("unknown", (), {})]) self.assertRaises(RuntimeError, str, path) def test_execute(self): class C(object): pass obj = C() obj.x = C() obj.x.y = lambda a, b: a+b path = Path(self.mock, None, [Action("getattr", ("x",), {}), Action("getattr", ("y",), {}), Action("call", (1,), {"b": 2})]) self.assertEquals(path.execute(obj), 3) class MatchParamsTest(TestCase): def true(self, *args): self.assertTrue(match_params(*args), repr(args)) def false(self, *args): self.assertFalse(match_params(*args), repr(args)) def test_any_repr(self): self.assertEquals(repr(ANY), "ANY") def test_any_equals(self): self.assertEquals(ANY, ANY) self.assertNotEquals(ANY, ARGS) self.assertNotEquals(ANY, object()) def test_any_matches(self): self.assertTrue(ANY.matches(1)) self.assertTrue(ANY.matches(42)) self.assertTrue(ANY.matches(object())) def test_is_repr(self): self.assertEquals(repr(IS("obj")), "IS('obj')") def test_is_equals(self): l1 = [] l2 = [] self.assertNotEquals(IS(l1), l2) self.assertEquals(IS(l1), IS(l1)) self.assertNotEquals(IS(l1), IS(l2)) def test_is_matches(self): l1 = [] l2 = [] self.assertTrue(IS(l1).matches(l1)) self.assertFalse(IS(l1).matches(l2)) self.assertFalse(IS(l1).matches(ANY)) def test_contains_repr(self): self.assertEquals(repr(CONTAINS("obj")), "CONTAINS('obj')") def test_contains_equals(self): self.assertEquals(CONTAINS([1]), CONTAINS([1])) self.assertNotEquals(CONTAINS(1), CONTAINS([1])) def test_contains_matches(self): self.assertTrue(CONTAINS(1).matches([1])) self.assertFalse(CONTAINS([1]).matches([1])) self.assertFalse(CONTAINS(1).matches(object())) def test_contains_matches_with_contains(self): """Can't be iterated, but has contains hook.""" class C(object): def __contains__(self, value): return True self.assertTrue(CONTAINS(1).matches(C())) def test_in_repr(self): self.assertEquals(repr(IN("obj")), "IN('obj')") def test_in_equals(self): self.assertEquals(IN([1]), IN([1])) self.assertNotEquals(IN([1]), IN(1)) def test_in_matches(self): self.assertTrue(IN([1]).matches(1)) self.assertFalse(IN([1]).matches([1])) self.assertFalse(IN([1]).matches(object())) def test_match_repr(self): self.assertEquals(repr(MATCH("obj")), "MATCH('obj')") def test_match_equals(self): obj1, obj2 = [], [] self.assertEquals(MATCH(obj1), MATCH(obj1)) self.assertNotEquals(MATCH(obj1), MATCH(obj2)) def test_match_matches(self): self.assertTrue(MATCH(lambda x: x > 10).matches(15)) self.assertFalse(MATCH(lambda x: x > 10).matches(5)) def test_normal(self): self.true((), {}, (), {}) self.true((1, 2), {"a": 3}, (1, 2), {"a": 3}) self.false((1,), {}, (), {}) self.false((), {}, (1,), {}) self.false((1, 2), {"a": 3}, (1, 2), {"a": 4}) self.false((1, 2), {"a": 3}, (1, 3), {"a": 3}) def test_any(self): self.true((1, 2), {"a": ANY}, (1, 2), {"a": 4}) self.true((1, ANY), {"a": 3}, (1, 3), {"a": 3}) self.false((ANY,), {}, (), {}) def test_special_args_matching(self): self.true((1, IN([2])), {}, (1, 2), {}) self.true((1, 2), {"a": IN([3])}, (1, 2), {"a": 3}) self.false((1, IN([2])), {}, (1, 3), {}) self.false((1, 2), {"a": IN([3])}, (1, 2), {"a": 4}) def test_args_alone(self): self.true((ARGS,), {}, (), {}) self.true((ARGS,), {}, (1, 2), {}) self.false((ARGS,), {}, (1, 2), {"a": 2}) self.false((ARGS,), {}, (), {"a": 2}) self.true((ARGS,), {"a": 1}, (), {"a": 1}) self.true((ARGS,), {"a": 1}, (1, 2), {"a": 1}) self.false((ARGS,), {"a": 1}, (), {"a": 1, "b": 2}) self.false((ARGS,), {"a": 1}, (1, 2), {"a": 1, "b": 2}) self.false((ARGS,), {"a": 1}, (), {}) def test_kwargs_alone(self): self.true((KWARGS,), {}, (), {}) self.false((KWARGS,), {}, (1, 2), {}) self.false((KWARGS,), {}, (1, 2), {"a": 2}) self.true((KWARGS,), {}, (), {"a": 2}) self.true((KWARGS,), {"a": 1}, (), {"a": 1}) self.false((KWARGS,), {"a": 1}, (1, 2), {"a": 1}) self.true((KWARGS,), {"a": 1}, (), {"a": 1, "b": 2}) self.false((KWARGS,), {"a": 1}, (1, 2), {"a": 1, "b": 2}) self.false((KWARGS,), {"a": 1}, (), {}) def test_args_kwargs(self): self.true((ARGS, KWARGS), {}, (), {}) self.true((ARGS, KWARGS), {}, (1, 2), {}) self.true((ARGS, KWARGS), {}, (1, 2), {"a": 2}) self.true((ARGS, KWARGS), {}, (), {"a": 2}) self.true((ARGS, KWARGS), {"a": 1}, (), {"a": 1}) self.true((ARGS, KWARGS), {"a": 1}, (1, 2), {"a": 1}) self.true((ARGS, KWARGS), {"a": 1}, (), {"a": 1, "b": 2}) self.true((ARGS, KWARGS), {"a": 1}, (1, 2), {"a": 1, "b": 2}) self.false((ARGS, KWARGS), {"a": 1}, (), {}) def test_args_at_start(self): self.true((ARGS, 3, 4), {}, (3, 4), {}) self.true((ARGS, 3, 4), {}, (1, 2, 3, 4), {}) self.true((ARGS, 3, 4), {"a": 1}, (3, 4), {"a": 1}) self.false((ARGS, 3, 4), {"a": 1}, (1, 2, 3, 4), {"a": 1, "b": 2}) self.false((ARGS, 3, 4), {}, (), {}) self.false((ARGS, 3, 4), {}, (3, 5), {}) self.false((ARGS, 3, 4), {}, (5, 5), {}) self.false((ARGS, 3, 4), {}, (3, 4, 5), {}) self.false((ARGS, 3, 4), {"a": 1}, (), {}) self.false((ARGS, 3, 4), {"a": 1}, (3, 4), {}) self.false((ARGS, 3, 4), {"a": 1}, (3, 4), {"b": 2}) def test_args_at_end(self): self.true((1, 2, ARGS), {}, (1, 2), {}) self.true((1, 2, ARGS), {}, (1, 2, 3, 4), {}) self.true((1, 2, ARGS), {"a": 1}, (1, 2), {"a": 1}) self.false((1, 2, ARGS), {"a": 1}, (1, 2, 3, 4), {"a": 1, "b": 2}) self.false((1, 2, ARGS), {}, (), {}) self.false((1, 2, ARGS), {}, (1, 3), {}) self.false((1, 2, ARGS), {}, (3, 3), {}) self.false((1, 2, ARGS), {"a": 1}, (), {}) self.false((1, 2, ARGS), {"a": 1}, (1, 2), {}) self.false((1, 2, ARGS), {"a": 1}, (1, 2), {"b": 2}) def test_args_at_middle(self): self.true((1, ARGS, 4), {}, (1, 4), {}) self.true((1, ARGS, 4), {}, (1, 2, 3, 4), {}) self.true((1, ARGS, 4), {"a": 1}, (1, 4), {"a": 1}) self.false((1, ARGS, 4), {"a": 1}, (1, 2, 3, 4), {"a": 1, "b": 2}) self.false((1, ARGS, 4), {}, (), {}) self.false((1, ARGS, 4), {}, (1, 5), {}) self.false((1, ARGS, 4), {}, (5, 5), {}) self.false((1, ARGS, 4), {"a": 1}, (), {}) self.false((1, ARGS, 4), {"a": 1}, (1, 4), {}) self.false((1, ARGS, 4), {"a": 1}, (1, 4), {"b": 2}) def test_args_multiple(self): self.true((ARGS, 3, ARGS, 6, ARGS), {}, (1, 2, 3, 4, 5, 6), {}) self.true((ARGS, ARGS, ARGS), {}, (1, 2, 3, 4, 5, 6), {}) self.true((ARGS, ARGS, ARGS), {}, (), {}) self.false((ARGS, 3, ARGS, 6, ARGS), {}, (1, 2, 3, 4, 5), {}) self.false((ARGS, 3, ARGS, 6, ARGS), {}, (1, 2, 4, 5, 6), {}) class MockTest(TestCase): def setUp(self): self.paths = [] class StubMocker(object): _recording = True def is_recording(self): return self._recording def replay(self): self._recording = False def act(_, path): self.paths.append(path) return 42 self.StubMocker = StubMocker self.mocker = StubMocker() self.mock = Mock(self.mocker) def test_default_attributes(self): self.assertEquals(self.mock.__mocker__, self.mocker) self.assertEquals(self.mock.__mocker_path__, Path(self.mock)) self.assertEquals(self.mock.__mocker_name__, None) self.assertEquals(self.mock.__mocker_spec__, None) self.assertEquals(self.mock.__mocker_type__, None) self.assertEquals(self.mock.__mocker_object__, None) self.assertEquals(self.mock.__mocker_passthrough__, False) self.assertEquals(self.mock.__mocker_patcher__, None) self.assertEquals(self.mock.__mocker_replace__, False) self.assertEquals(self.mock.__mocker_count__, True) def test_path(self): path = object() self.assertEquals(Mock(self.mocker, path).__mocker_path__, path) def test_object(self): mock = Mock(self.mocker, object="foo") self.assertEquals(mock.__mocker_object__, "foo") self.assertEquals(mock.__mocker_path__.root_object, "foo") def test_passthrough(self): mock = Mock(self.mocker, object="foo", passthrough=True) self.assertEquals(mock.__mocker_object__, "foo") self.assertEquals(mock.__mocker_passthrough__, True) def test_spec(self): C = object() self.assertEquals(Mock(self.mocker, spec=C).__mocker_spec__, C) def test_class_without_type(self): mock = Mock(self.mocker) self.assertEquals(mock.__class__, Mock) self.mocker.replay() self.assertEquals(mock.__class__, Mock) def test_class_with_type_when_recording(self): class C(object): pass mock = Mock(self.mocker, type=C) self.assertEquals(mock.__mocker_type__, C) self.assertEquals(mock.__class__, Mock) self.assertEquals(isinstance(mock, Mock), True) def test_class_with_type_when_replaying(self): class C(object): pass mock = Mock(self.mocker, type=C) self.mocker.replay() self.assertEquals(mock.__mocker_type__, C) self.assertEquals(mock.__class__, C) self.assertEquals(isinstance(mock, C), True) def test_auto_naming(self): named_mock = self.mock named_mock.attr another_name = named_mock named_mock = None # Can't find this one anymore. another_name.attr self.assertEquals(another_name.__mocker_name__, "named_mock") def test_auto_naming_on_self(self): self.named_mock = self.mock del self.mock self.named_mock.attr self.assertEquals(self.named_mock.__mocker_name__, "named_mock") def test_auto_naming_on_bad_self(self): self_ = self self = object() # No __dict__ self_.named_mock = self_.mock self_.named_mock.attr self_.assertEquals(self_.named_mock.__mocker_name__, None) def test_auto_naming_without_getframe(self): getframe = sys._getframe sys._getframe = None try: self.named_mock = self.mock self.named_mock.attr self.assertEquals(self.named_mock.__mocker_name__, None) finally: sys._getframe = getframe def test_getattr(self): self.assertEquals(self.mock.attr, 42) (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("getattr", ("attr",), {})) def test_setattr(self): self.mock.attr = 24 (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("setattr", ("attr", 24), {})) def test_delattr(self): del self.mock.attr (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("delattr", ("attr",), {})) def test_call(self): self.mock(1, a=2) (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("call", (1,), {"a": 2})) def test_contains(self): self.assertEquals("value" in self.mock, True) # True due to 42. (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("contains", ("value",), {})) def test_getitem(self): self.assertEquals(self.mock["key"], 42) (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("getitem", ("key",), {})) def test_setitem(self): self.mock["key"] = "value" (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("setitem", ("key", "value"), {})) def test_delitem(self): del self.mock["key"] (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("delitem", ("key",), {})) def test_len(self): self.assertEquals(len(self.mock), 42) (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("len", (), {})) def test_len_with_mock_result(self): self.mocker.act = lambda path: Mock(self.mocker) self.assertEquals(len(self.mock), 0) def test_len_transforms_match_error_to_attribute_error(self): """ list() uses len() as a hint. When we mock iter(), it shouldn't explode due to the lack of len(). """ def raise_error(path): raise MatchError("Kaboom!") self.mocker.act = raise_error try: len(self.mock) except AttributeError, e: self.assertEquals(str(e), "Kaboom!") except MatchError: self.fail("Expected AttributeError, not MatchError.") else: self.fail("AttributeError not raised.") def test_mock_raises_attribute_error_on_length_hint(self): """ In Python 2.6+ list() uses __length_hint__() as a hint. When we mock iter(), it shouldn't explode due to the lack of __length_hint__. """ def raise_error(path): raise MatchError("Kaboom!") self.mocker.act = raise_error try: self.mock.__length_hint__ except AttributeError, e: self.assertEquals(str(e), "No __length_hint__ here!") except MatchError: self.fail("Expected AttributeError, not MatchError.") else: self.fail("AttributeError not raised.") def test_nonzero(self): self.assertEquals(bool(self.mock), True) # True due to 42. (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("nonzero", (), {})) def test_nonzero_returns_true_on_match_error(self): """ When an object doesn't define a boolean behavior explicitly, it should be handled as a true value by default, as Python usually does. """ def raise_error(path): raise MatchError("Kaboom!") self.mocker.act = raise_error self.assertEquals(bool(self.mock), True) def test_nonzero_with_mock_result(self): self.mocker.act = lambda path: Mock(self.mocker) self.assertEquals(bool(self.mock), True) def test_iter(self): result_mock = Mock(self.mocker) self.mocker.act = lambda path: self.paths.append(path) or result_mock result = iter(self.mock) self.assertEquals(type(result), type(iter([]))) self.assertEquals(list(result), []) (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertEquals(path, self.mock.__mocker_path__ + Action("iter", (), {})) def test_passthrough_on_unexpected(self): class StubMocker(object): def act(self, path): if path.actions[-1].args == ("x",): raise MatchError return 42 class C(object): x = 123 y = 321 mock = Mock(StubMocker(), object=C()) self.assertRaises(MatchError, getattr, mock, "x", 42) self.assertEquals(mock.y, 42) mock = Mock(StubMocker(), passthrough=True) self.assertRaises(MatchError, getattr, mock, "x", 42) self.assertEquals(mock.y, 42) mock = Mock(StubMocker(), object=C(), passthrough=True) self.assertEquals(mock.x, 123) self.assertEquals(mock.y, 42) mock = Mock(StubMocker(), passthrough=True) act = mock.__mocker_act__ self.assertEquals(act("getattr", ("x",), 42, object=C()), 123) self.assertEquals(act("getattr", ("y",), 42, object=C()), 42) def test_act_with_object(self): obj = object() self.mock.__mocker_act__("kind", object=obj) (path,) = self.paths self.assertEquals(type(path), Path) self.assertTrue(path.parent_path is self.mock.__mocker_path__) self.assertTrue(path.root_object is obj) def test_reraise_assertion(self): class StubMocker(object): def act(self, path): message = os.linesep.join(["An", "- error", "- happened"]) raise AssertionError(message) mock = Mock(StubMocker()) try: mock.__mocker_act__("kind") except AssertionError, e: message = os.linesep.join(["[Mocker] Unmet expectation:", "", "=> An", " - error", " - happened", ""]) self.assertEquals(str(e), message) else: self.fail("AssertionError not raised") def test_action_execute_and_path_str(self): """Check for kind support on Action.execute() and Path.__str__().""" mocker = Mocker() check = [] for name, attr in Mock.__dict__.iteritems(): if not name.startswith("__mocker_") and hasattr(attr, "__call__"): mock = mocker.mock() args = ["arg"] * (attr.func_code.co_argcount - 1) try: attr(mock, *args) except: pass else: path = mocker.get_events()[-1].path check.append((path, path.actions[-1])) for path, action in check: kind = action.kind try: str(path) except RuntimeError: self.fail("Kind %r not supported by Path.__str__()" % kind) try: action.execute(object()) except RuntimeError: self.fail("Kind %r not supported by Action.execute()" % kind) except: pass class EventTest(TestCase): def setUp(self): self.event = Event() def test_default_path(self): self.assertEquals(self.event.path, None) def test_path(self): path = object() event = Event(path) self.assertEquals(event.path, path) def test_add_and_get_tasks(self): task1 = self.event.add_task(Task()) task2 = self.event.add_task(Task()) self.assertEquals(self.event.get_tasks(), [task1, task2]) def test_remove_task(self): task1 = self.event.add_task(Task()) task2 = self.event.add_task(Task()) task3 = self.event.add_task(Task()) self.event.remove_task(task2) self.assertEquals(self.event.get_tasks(), [task1, task3]) def test_default_matches(self): self.assertEquals(self.event.matches(None), False) def test_default_run(self): self.assertEquals(self.event.run(None), None) def test_default_satisfied(self): self.assertEquals(self.event.satisfied(), True) def test_default_verify(self): self.assertEquals(self.event.verify(), None) def test_default_replay(self): self.assertEquals(self.event.replay(), None) def test_default_restore(self): self.assertEquals(self.event.restore(), None) def test_matches_false(self): task1 = self.event.add_task(Task()) task1.matches = lambda path: True task2 = self.event.add_task(Task()) task2.matches = lambda path: False task3 = self.event.add_task(Task()) task3.matches = lambda path: True self.assertEquals(self.event.matches(None), False) def test_matches_true(self): task1 = self.event.add_task(Task()) task1.matches = lambda path: True task2 = self.event.add_task(Task()) task2.matches = lambda path: True self.assertEquals(self.event.matches(None), True) def test_matches_argument(self): calls = [] task = self.event.add_task(Task()) task.matches = lambda path: calls.append(path) self.event.matches(42) self.assertEquals(calls, [42]) def test_run(self): calls = [] task1 = self.event.add_task(Task()) task1.run = lambda path: calls.append(path) or True task2 = self.event.add_task(Task()) task2.run = lambda path: calls.append(path) or False task3 = self.event.add_task(Task()) task3.run = lambda path: calls.append(path) or None self.assertEquals(self.event.run(42), False) self.assertEquals(calls, [42, 42, 42]) def test_run_errors(self): class MyTask(object): def __init__(self, id, failed): self.id = id self.failed = failed def run(self, path): if self.failed: raise AssertionError("%d failed" % self.id) event = Event("i.am.a.path") event.add_task(MyTask(1, True)) event.add_task(MyTask(2, False)) event.add_task(MyTask(3, True)) try: event.run("i.am.a.path") except AssertionError, e: message = os.linesep.join(["i.am.a.path", "- 1 failed", "- 3 failed"]) self.assertEquals(str(e), message) else: self.fail("AssertionError not raised") def test_run_errors_with_different_path_representation(self): """When the path representation isn't the same it's shown up.""" class MyTask(object): def __init__(self, id, failed): self.id = id self.failed = failed def run(self, path): if self.failed: raise AssertionError("%d failed" % self.id) event = Event("i.am.a.path") event.add_task(MyTask(1, True)) event.add_task(MyTask(2, False)) event.add_task(MyTask(3, True)) try: event.run(42) except AssertionError, e: message = os.linesep.join(["i.am.a.path", "- Run: 42", # <== "- 1 failed", "- 3 failed"]) self.assertEquals(str(e), message) else: self.fail("AssertionError not raised") def test_run_errors_need_good_messages(self): class MyTask(Task): def run(self, path): raise AssertionError() self.event.add_task(MyTask()) self.assertRaises(RuntimeError, self.event.run, 42) def test_has_run(self): self.assertFalse(self.event.has_run()) self.event.run(None) self.assertTrue(self.event.has_run()) def test_has_run_reset_on_replay(self): self.event.run(None) self.event.replay() self.assertFalse(self.event.has_run()) def test_may_run(self): calls = [] task1 = Task() task1.may_run = lambda path: calls.append((1, path)) or True task2 = Task() task2.may_run = lambda path: calls.append((2, path)) self.assertEquals(self.event.may_run(42), True) self.event.add_task(task1) self.assertEquals(self.event.may_run(42), True) self.assertEquals(calls, [(1, 42)]) del calls[:] self.event.add_task(task2) self.event.add_task(task1) # Should return on first false. self.assertEquals(self.event.may_run(42), False) self.assertEquals(calls, [(1, 42), (2, 42)]) def test_satisfied_false(self): def raise_error(): raise AssertionError("An error") task1 = self.event.add_task(Task()) task2 = self.event.add_task(Task()) task2.verify = raise_error task3 = self.event.add_task(Task()) self.assertEquals(self.event.satisfied(), False) def test_satisfied_true(self): task1 = self.event.add_task(Task()) task1.satisfied = lambda: True task2 = self.event.add_task(Task()) task2.satisfied = lambda: True self.assertEquals(self.event.satisfied(), True) def test_verify(self): class MyTask(object): def __init__(self, id, failed): self.id = id self.failed = failed def verify(self): if self.failed: raise AssertionError("%d failed" % self.id) event = Event("i.am.a.path") event.add_task(MyTask(1, True)) event.add_task(MyTask(2, False)) event.add_task(MyTask(3, True)) try: event.verify() except AssertionError, e: message = os.linesep.join(["i.am.a.path", "- 1 failed", "- 3 failed"]) self.assertEquals(str(e), message) else: self.fail("AssertionError not raised") def test_verify_errors_need_good_messages(self): class MyTask(Task): def verify(self): raise AssertionError() self.event.add_task(MyTask()) self.assertRaises(RuntimeError, self.event.verify) def test_replay(self): calls = [] task1 = self.event.add_task(Task()) task2 = self.event.add_task(Task()) task1.replay = lambda: calls.append("task1") task2.replay = lambda: calls.append("task2") self.event.replay() self.assertEquals(calls, ["task1", "task2"]) def test_restore(self): calls = [] task1 = self.event.add_task(Task()) task2 = self.event.add_task(Task()) task1.restore = lambda: calls.append("task1") task2.restore = lambda: calls.append("task2") self.event.restore() self.assertEquals(calls, ["task1", "task2"]) class ReplayRestoreEventTest(TestCase): def setUp(self): self.event = ReplayRestoreEvent() def test_never_matches(self): self.assertEquals(self.event.matches(None), False) self.event.add_task(Task()) self.assertEquals(self.event.matches(None), False) class TaskTest(TestCase): def setUp(self): self.task = Task() def test_default_matches(self): self.assertEquals(self.task.matches(None), True) def test_default_may_run(self): self.assertEquals(self.task.may_run(None), True) def test_default_run(self): self.assertEquals(self.task.run(None), None) def test_default_verify(self): self.assertEquals(self.task.verify(), None) def test_default_replay(self): self.assertEquals(self.task.replay(), None) def test_default_restore(self): self.assertEquals(self.task.restore(), None) class OnRestoreCallerTest(TestCase): def setUp(self): self.mocker = CleanMocker() self.mock = self.mocker.mock() def test_is_task(self): self.assertTrue(isinstance(OnRestoreCaller(None), Task)) def test_restore(self): calls = [] task = OnRestoreCaller(lambda: calls.append("callback")) self.assertEquals(calls, []) task.restore() self.assertEquals(calls, ["callback"]) task.restore() self.assertEquals(calls, ["callback", "callback"]) class PathMatcherTest(TestCase): def setUp(self): self.mocker = CleanMocker() self.mock = self.mocker.mock() def test_is_task(self): self.assertTrue(isinstance(PathMatcher(None), Task)) def test_create(self): path = object() task = PathMatcher(path) self.assertEquals(task.path, path) def test_matches(self): path = Path(self.mock, None, [Action("getattr", ("attr1",), {})]) task = PathMatcher(path) action = Action("getattr", (), {}, Path(self.mock)) self.assertFalse(task.matches(action.path + action)) action = Action("getattr", ("attr1",), {}, Path(self.mock)) self.assertTrue(task.matches(action.path + action)) def test_recorder(self): path = Path(self.mock, [Action("call", (), {})]) event = Event(path) path_matcher_recorder(self.mocker, event) (task,) = event.get_tasks() self.assertEquals(type(task), PathMatcher) self.assertTrue(task.path is path) def test_is_standard_recorder(self): self.assertTrue(path_matcher_recorder in Mocker.get_recorders()) class RunCounterTest(TestCase): def setUp(self): self.mocker = CleanMocker() self.mock = self.mocker.mock() self.action = Action("getattr", ("attr",), {}, Path(self.mock)) self.path = Path(self.mock, [self.action]) self.event = Event(self.path) def test_is_task(self): self.assertTrue(isinstance(RunCounter(1), Task)) def test_create_one_argument(self): task = RunCounter(2) self.assertEquals(task.min, 2) self.assertEquals(task.max, 2) def test_create_min_max(self): task = RunCounter(2, 3) self.assertEquals(task.min, 2) self.assertEquals(task.max, 3) def test_create_unbounded(self): task = RunCounter(2, None) self.assertEquals(task.min, 2) self.assertEquals(task.max, sys.maxint) def test_run_one_argument(self): task = RunCounter(2) task.run(self.path) task.run(self.path) self.assertRaises(AssertionError, task.run, self.path) def test_run_two_arguments(self): task = RunCounter(1, 2) task.run(self.path) task.run(self.path) self.assertRaises(AssertionError, task.run, self.path) def test_may_run(self): task = RunCounter(1) self.assertEquals(task.may_run(None), True) task.run(self.path) self.assertEquals(task.may_run(None), False) def test_verify(self): task = RunCounter(2) self.assertRaises(AssertionError, task.verify) task.run(self.path) self.assertRaises(AssertionError, task.verify) task.run(self.path) task.verify() self.assertRaises(AssertionError, task.run, self.path) self.assertRaises(AssertionError, task.verify) def test_verify_two_arguments(self): task = RunCounter(1, 2) self.assertRaises(AssertionError, task.verify) task.run(self.path) task.verify() task.run(self.path) task.verify() self.assertRaises(AssertionError, task.run, self.path) self.assertRaises(AssertionError, task.verify) def test_verify_unbound(self): task = RunCounter(1, None) self.assertRaises(AssertionError, task.verify) task.run(self.path) task.verify() task.run(self.path) task.verify() def test_reset_on_replay(self): task = RunCounter(1, 1) task.run(self.path) self.assertRaises(AssertionError, task.run, self.path) task.replay() self.assertRaises(AssertionError, task.verify) task.run(self.path) self.assertRaises(AssertionError, task.run, self.path) def test_recorder(self): run_counter_recorder(self.mocker, self.event) (task,) = self.event.get_tasks() self.assertEquals(type(task), ImplicitRunCounter) self.assertTrue(task.min == 1) self.assertTrue(task.max == 1) def test_recorder_wont_record_when_count_is_false(self): self.mock.__mocker_count__ = False run_counter_recorder(self.mocker, self.event) self.assertEquals(self.event.get_tasks(), []) def test_removal_recorder(self): """ Events created by getattr actions which lead to other events may be repeated any number of times. """ path1 = Path(self.mock) path2 = path1 + Action("getattr", ("attr",), {}, path1) path3 = path2 + Action("getattr", ("attr",), {}, path2) path4 = path3 + Action("call", (), {}, path3) path5 = path4 + Action("call", (), {}, path4) event3 = self.mocker.add_event(Event(path3)) event2 = self.mocker.add_event(Event(path2)) event5 = self.mocker.add_event(Event(path5)) event4 = self.mocker.add_event(Event(path4)) event2.add_task(RunCounter(1)) event2.add_task(ImplicitRunCounter(1)) event2.add_task(RunCounter(1)) event3.add_task(RunCounter(1)) event3.add_task(ImplicitRunCounter(1)) event3.add_task(RunCounter(1)) event4.add_task(RunCounter(1)) event4.add_task(ImplicitRunCounter(1)) event4.add_task(RunCounter(1)) event5.add_task(RunCounter(1)) event5.add_task(ImplicitRunCounter(1)) event5.add_task(RunCounter(1)) # First, when the previous event isn't a getattr. run_counter_removal_recorder(self.mocker, event5) self.assertEquals(len(event2.get_tasks()), 3) self.assertEquals(len(event3.get_tasks()), 3) self.assertEquals(len(event4.get_tasks()), 3) self.assertEquals(len(event5.get_tasks()), 3) # Now, for real. run_counter_removal_recorder(self.mocker, event4) self.assertEquals(len(event2.get_tasks()), 3) self.assertEquals(len(event3.get_tasks()), 2) self.assertEquals(len(event4.get_tasks()), 3) self.assertEquals(len(event5.get_tasks()), 3) task1, task2 = event3.get_tasks() self.assertEquals(type(task1), RunCounter) self.assertEquals(type(task2), RunCounter) def test_removal_recorder_with_obj(self): self.mocker.add_recorder(run_counter_recorder) self.mocker.add_recorder(run_counter_removal_recorder) obj = self.mocker.mock() obj.x.y()() events = self.mocker.get_events() self.assertEquals(len(events), 4) self.assertEquals(len(events[0].get_tasks()), 0) self.assertEquals(len(events[1].get_tasks()), 0) self.assertEquals(len(events[2].get_tasks()), 1) self.assertEquals(len(events[3].get_tasks()), 1) def test_reset_on_replay_with_mock(self): mock = self.mocker.mock() mock() self.mocker.count(1) self.mocker.replay() mock() self.assertRaises(AssertionError, mock) self.mocker.replay() mock() self.assertRaises(AssertionError, mock) def test_is_standard_recorder(self): self.assertTrue(run_counter_recorder in Mocker.get_recorders()) self.assertTrue(run_counter_removal_recorder in Mocker.get_recorders()) class MockReturnerTest(TestCase): def setUp(self): self.mocker = CleanMocker() self.mock = self.mocker.mock() self.action = Action("getattr", ("attr",), {}, Path(self.mock)) self.path = Path(self.mock, [self.action]) self.event = Event(self.path) def test_is_task(self): self.assertTrue(isinstance(MockReturner(self.mocker), Task)) def test_create(self): task = MockReturner(self.mocker) mock = task.run(self.path) self.assertTrue(isinstance(mock, Mock)) self.assertEquals(mock.__mocker__, self.mocker) self.assertTrue(mock.__mocker_path__.matches(self.path)) def test_recorder(self): path1 = Path(self.mock) path2 = path1 + Action("getattr", ("attr",), {}, path1) path3 = path2 + Action("getattr", ("attr",), {}, path2) path4 = path3 + Action("call", (), {}, path3) event2 = self.mocker.add_event(Event(path2)) event3 = self.mocker.add_event(Event(path3)) event4 = self.mocker.add_event(Event(path4)) self.assertEquals(len(event2.get_tasks()), 0) self.assertEquals(len(event3.get_tasks()), 0) self.assertEquals(len(event4.get_tasks()), 0) # Calling on 4 should add it only to the parent. mock_returner_recorder(self.mocker, event4) self.assertEquals(len(event2.get_tasks()), 0) self.assertEquals(len(event3.get_tasks()), 1) self.assertEquals(len(event4.get_tasks()), 0) (task,) = event3.get_tasks() self.assertEquals(type(task), MockReturner) self.assertEquals(task.mocker, self.mocker) # Calling on it again shouldn't do anything. mock_returner_recorder(self.mocker, event4) self.assertEquals(len(event2.get_tasks()), 0) self.assertEquals(len(event3.get_tasks()), 1) self.assertEquals(len(event4.get_tasks()), 0) def test_is_standard_recorder(self): self.assertTrue(mock_returner_recorder in Mocker.get_recorders()) class FunctionRunnerTest(TestCase): def setUp(self): self.mocker = CleanMocker() self.mock = self.mocker.mock() self.action = Action("call", (1, 2), {"c": 3}, Path(self.mock)) self.path = Path(self.mock, None, [self.action]) self.event = Event(self.path) def test_is_task(self): self.assertTrue(isinstance(FunctionRunner(None), Task)) def test_run(self): task = FunctionRunner(lambda *args, **kwargs: repr((args, kwargs))) result = task.run(self.path) self.assertEquals(result, "((1, 2), {'c': 3})") class PathExecuterTest(TestCase): def setUp(self): self.mocker = CleanMocker() def test_is_task(self): self.assertTrue(isinstance(PathExecuter(), Task)) def test_run(self): class C(object): pass obj = C() obj.x = C() obj.x.y = lambda a, b: a+b path = Path(None, obj, [Action("getattr", ("x",), {}), Action("getattr", ("y",), {}), Action("call", (1,), {"b": 2})]) task = PathExecuter() self.assertEquals(task.run(path), 3) def test_run_with_result_callback(self): class C(object): def x(self, arg): return 41 + arg obj = C() path = Path(None, obj, [Action("getattr", ("x",), {}), Action("call", (1,), {})]) calls = [] result_callback = lambda result: calls.append(result) task = PathExecuter(result_callback) self.assertEquals(task.get_result_callback(), result_callback) self.assertEquals(task.run(path), 42) self.assertEquals(calls, [42]) class OrdererTest(TestCase): def setUp(self): self.mocker = CleanMocker() self.mock = self.mocker.mock() self.action = Action("call", (1, 2, Path(self.mock)), {"c": 3}) self.path = Path(self.mock, [self.action]) def test_is_task(self): self.assertTrue(isinstance(Orderer(self.path), Task)) def test_path(self): self.assertEquals(Orderer(self.path).path, self.path) def test_has_run(self): orderer = Orderer(self.path) self.assertFalse(orderer.has_run()) orderer.run(self.path) self.assertTrue(orderer.has_run()) def test_reset_on_replay(self): orderer = Orderer(self.path) orderer.run(self.path) orderer.replay() self.assertFalse(orderer.has_run()) def test_reset_on_replay_with_mock(self): self.mocker.add_recorder(path_matcher_recorder) mock = self.mocker.mock() self.mocker.order(mock(1), mock(2)) self.mocker.replay() mock(1) mock(2) self.mocker.replay() self.assertRaises(AssertionError, mock, 2) def test_add_and_get_dependencies(self): orderer = Orderer(self.path) orderer.add_dependency(1) orderer.add_dependency(2) self.assertEquals(orderer.get_dependencies(), [1, 2]) def test_may_run(self): orderer1 = Orderer(self.path) orderer2 = Orderer(self.path) orderer2.add_dependency(orderer1) self.assertFalse(orderer2.may_run(None)) self.assertTrue(orderer1.may_run(None)) orderer1.run(self.path) self.assertTrue(orderer2.may_run(None)) def test_run_with_missing_dependency(self): orderer1 = Orderer("path1") orderer2 = Orderer("path2") orderer2.add_dependency(orderer1) try: orderer2.run(None) except AssertionError, e: self.assertEquals(str(e), "Should be after: path1") else: self.fail("AssertionError not raised") class SpecCheckerTest(TestCase): def setUp(self): class C(object): def __call__(self, a, b, c=3): pass def normal(self, a, b, c=3): pass def varargs(self, a, b, c=3, *args): pass def varkwargs(self, a, b, c=3, **kwargs): pass def varargskwargs(self, a, b, c=3, *args, **kwargs): pass def klass(cls, a, b, c=3): pass klass = classmethod(klass) def static(a, b, c=3): pass static = staticmethod(static) def noargs(self): pass def klassnoargs(cls): pass klassnoargs = classmethod(klassnoargs) def staticnoargs(): pass staticnoargs = staticmethod(staticnoargs) self.cls = C self.mocker = CleanMocker() self.mock = self.mocker.mock(self.cls) def path(self, *args, **kwargs): action = Action("call", args, kwargs, Path(self.mock)) return action.path + action def good(self, method_names, args_expr): if type(method_names) is not list: method_names = [method_names] for method_name in method_names: task = SpecChecker(getattr(self.cls, method_name, None)) path = eval("self.path(%s)" % args_expr) self.assertEquals(task.may_run(path), True) try: task.run(path) except AssertionError: self.fail("AssertionError raised with self.cls.%s(%s)" % (method_name, args_expr)) def bad(self, method_names, args_expr): if type(method_names) is not list: method_names = [method_names] for method_name in method_names: task = SpecChecker(getattr(self.cls, method_name, None)) path = eval("self.path(%s)" % args_expr) self.assertEquals(task.may_run(path), False) try: task.run(path) except AssertionError: pass else: self.fail("AssertionError not raised with self.cls.%s(%s)" % (method_name, args_expr)) def test_get_method(self): task = SpecChecker(self.cls.noargs) self.assertEquals(task.get_method(), self.cls.noargs) def test_is_standard_recorder(self): self.assertTrue(spec_checker_recorder in Mocker.get_recorders()) def test_is_task(self): self.assertTrue(isinstance(SpecChecker(self.cls.normal), Task)) def test_error_message(self): task = SpecChecker(self.cls.normal) try: task.run(self.path(1)) except AssertionError, e: self.assertEquals(str(e), "Specification is normal(a, b, c=3): " "'b' not provided") else: self.fail("AssertionError not raised") def test_verify_unexistent_method(self): task = SpecChecker(None) try: task.verify() except AssertionError, e: self.assertEquals(str(e), "Method not found in real specification") else: self.fail("AssertionError not raised") def test_unsupported_object_for_getargspec(self): from zlib import adler32 # If that fails, this test has to change because either adler32 has # changed, or the implementation of getargspec has changed. self.assertRaises(TypeError, inspect.getargspec, adler32) try: task = SpecChecker(adler32) task.run(self.path("asd")) except TypeError, e: self.fail("TypeError: %s" % str(e)) def test_recorder(self): self.mocker.add_recorder(spec_checker_recorder) obj = self.mocker.mock(spec=self.cls) obj.noargs() getattr, call = self.mocker.get_events() self.assertEquals(getattr.get_tasks(), []) (task,) = call.get_tasks() self.assertEquals(type(task), SpecChecker) self.assertEquals(task.get_method(), self.cls.noargs) def test_recorder_with_unexistent_method(self): self.mocker.add_recorder(spec_checker_recorder) obj = self.mocker.mock(spec=self.cls) obj.unexistent() getattr, call = self.mocker.get_events() self.assertEquals(getattr.get_tasks(), []) (task,) = call.get_tasks() self.assertEquals(type(task), SpecChecker) self.assertEquals(task.get_method(), None) def test_recorder_second_action_isnt_call(self): self.mocker.add_recorder(spec_checker_recorder) obj = self.mocker.mock(spec=self.cls) obj.noargs.x event1, event2 = self.mocker.get_events() self.assertEquals(event1.get_tasks(), []) self.assertEquals(event2.get_tasks(), []) def test_recorder_first_action_isnt_getattr(self): self.mocker.add_recorder(spec_checker_recorder) obj = self.mocker.mock(spec=self.cls) obj.__mocker_act__("anyother", ("attr",))() event1, event2 = self.mocker.get_events() self.assertEquals(event1.get_tasks(), []) self.assertEquals(event2.get_tasks(), []) def test_recorder_more_than_two_actions(self): self.mocker.add_recorder(spec_checker_recorder) obj = self.mocker.mock(spec=self.cls) obj.noargs().x event1, event2, event3 = self.mocker.get_events() self.assertEquals(len(event1.get_tasks()), 0) self.assertEquals(len(event2.get_tasks()), 1) self.assertEquals(len(event3.get_tasks()), 0) def test_recorder_with_call_on_object(self): self.mocker.add_recorder(spec_checker_recorder) obj = self.mocker.mock(spec=self.cls) obj() (call,) = self.mocker.get_events() (task,) = call.get_tasks() self.assertEquals(type(task), SpecChecker) self.assertEquals(task.get_method(), self.cls.__call__) def test_recorder_more_than_one_action_with_direct_call(self): self.mocker.add_recorder(spec_checker_recorder) obj = self.mocker.mock(spec=self.cls) obj().x event1, event2 = self.mocker.get_events() self.assertEquals(len(event1.get_tasks()), 1) self.assertEquals(len(event2.get_tasks()), 0) def test_noargs(self): methods = ["noargs", "klassnoargs", "staticnoargs"] self.good(methods, "") self.bad(methods, "1") self.bad(methods, "a=1") def test_args_and_kwargs(self): methods = ["__call__", "normal", "varargs", "varkwargs", "varargskwargs", "static", "klass"] self.good(methods, "1, 2") self.good(methods, "1, 2, 3") self.good(methods, "1, b=2") self.good(methods, "1, b=2, c=3") self.good(methods, "a=1, b=2") self.good(methods, "a=1, b=2, c=3") def test_too_much(self): methods = ["__call__", "normal", "static", "klass"] self.bad(methods, "1, 2, 3, 4") self.bad(methods, "1, 2, d=4") def test_missing(self): methods = ["__call__", "normal", "varargs", "varkwargs", "varargskwargs", "static", "klass"] self.bad(methods, "") self.bad(methods, "1") self.bad(methods, "c=3") self.bad(methods, "a=1") self.bad(methods, "b=2, c=3") def test_duplicated_argument(self): methods = ["__call__", "normal", "varargs", "varkwargs", "varargskwargs", "static", "klass"] self.bad(methods, "1, 2, b=2") def test_varargs(self): self.good("varargs", "1, 2, 3, 4") self.bad("varargs", "1, 2, 3, 4, d=3") def test_varkwargs(self): self.good("varkwargs", "1, 2, d=3") self.bad("varkwargs", "1, 2, 3, 4, d=3") def test_varargskwargs(self): self.good("varargskwargs", "1, 2, 3, 4, d=3") def test_unexistent(self): self.bad("unexistent", "") class ProxyReplacerTest(TestCase): def setUp(self): self.mocker = CleanMocker() import calendar self.mock = Mock(self.mocker, object=calendar) self.task = ProxyReplacer(self.mock) def tearDown(self): self.task.restore() def test_is_task(self): self.assertTrue(isinstance(ProxyReplacer(None), Task)) def test_mock(self): mock = object() task = ProxyReplacer(mock) self.assertEquals(task.mock, mock) def test_defaults_to_not_installed(self): import calendar self.assertEquals(type(calendar), ModuleType) def test_install(self): self.task.replay() import calendar self.assertEquals(type(calendar), Mock) self.assertTrue(calendar is self.mock) def test_install_protects_mock(self): self.task.replay() self.assertEquals(type(self.mock.__mocker_object__), ModuleType) def test_install_protects_path(self): self.task.replay() self.assertEquals(type(self.mock.__mocker_path__.root_object), ModuleType) def test_deinstall_protects_task(self): self.task.replay() self.task.restore() self.assertEquals(type(self.task.mock), Mock) def test_install_protects_anything_with_mocker_replace_false(self): class C(object): def __init__(self): import calendar self.calendar = calendar self.__mocker_replace__ = False obj = C() self.task.replay() self.assertEquals(type(self.mock.__mocker_path__.root_object), ModuleType) def test_install_on_object(self): class C(object): def __init__(self): import calendar self.calendar = calendar obj = C() self.task.replay() self.assertEquals(type(obj.calendar), Mock) self.assertTrue(obj.calendar is self.mock) def test_install_on_submodule(self): from os import path import os mock = Mock(self.mocker, object=path) task = ProxyReplacer(mock) task.replay() try: self.assertEquals(type(os.path), Mock) self.assertTrue(os.path is mock) finally: task.restore() def test_uninstall_on_restore(self): self.task.replay() self.task.restore() import calendar self.assertEquals(type(calendar), ModuleType) self.assertEquals(calendar.__name__, "calendar") def test_uninstall_from_object(self): class C(object): def __init__(self): import calendar self.calendar = calendar obj = C() self.task.replay() self.task.restore() self.assertEquals(type(obj.calendar), ModuleType) self.assertEquals(obj.calendar.__name__, "calendar") def test_uninstall_from_submodule(self): from os import path import os mock = Mock(self.mocker, object=path) task = ProxyReplacer(mock) self.assertEquals(type(os.path), ModuleType) task.replay() task.restore() self.assertEquals(type(os.path), ModuleType) class PatcherTest(TestCase): def setUp(self): self.mocker = Mocker() self.patcher = Patcher() self.C = type("C", (object,), {}) self.D = type("D", (self.C,), {}) self.E = type("E", (), {}) class MockStub(object): def __mocker_act__(self, kind, args=(), kwargs={}, object=None): return (kind, args, kwargs, object) self.MockStub = MockStub def test_is_task(self): self.assertTrue(isinstance(Patcher(), Task)) def test_undefined_repr(self): self.assertEquals(repr(Undefined), "Undefined") def test_is_monitoring_unseen_class_kind(self): self.assertFalse(self.patcher.is_monitoring(self.C, "kind")) def test_monitor_class(self): self.patcher.monitor(self.C, "kind") self.assertTrue(self.patcher.is_monitoring(self.C, "kind")) def test_monitor_subclass(self): self.patcher.monitor(self.C, "kind") self.assertTrue(self.patcher.is_monitoring(self.D, "kind")) def test_monitor_unknown_class(self): self.patcher.monitor(self.C, "kind") self.assertFalse(self.patcher.is_monitoring(self.E, "kind")) def test_is_monitoring_unseen_instance(self): obj = self.E() self.patcher.monitor(self.C, "kind") self.assertFalse(self.patcher.is_monitoring(obj, "kind")) def test_is_monitoring_instance_explicitly_monitored(self): obj = self.C() self.patcher.monitor(obj, "kind") self.assertTrue(self.patcher.is_monitoring(obj, "kind")) def test_is_monitoring_instance_monitored_by_class(self): obj = self.D() self.patcher.monitor(self.D, "kind") self.assertTrue(self.patcher.is_monitoring(obj, "kind")) def test_patch_attr(self): self.patcher.patch_attr(self.C, "attr", "patch") self.assertEquals(self.C.__dict__.get("attr"), "patch") def test_patch_attr_and_restore(self): self.patcher.patch_attr(self.C, "attr", "patch") self.patcher.restore() self.assertTrue("attr" not in self.C.__dict__) def test_patch_attr_and_restore_to_original(self): self.C.attr = "original" self.patcher.patch_attr(self.C, "attr", "patch") self.patcher.restore() self.assertEquals(self.C.__dict__.get("attr"), "original") def test_get_unpatched_attr_unpatched_undefined(self): self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"), Undefined) def test_get_unpatched_attr_unpatched(self): self.C.attr = "original" self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"), "original") def test_get_unpatched_attr_defined_on_superclass(self): self.C.attr = "original" self.assertEquals(self.patcher.get_unpatched_attr(self.D, "attr"), "original") def test_get_unpatched_attr_defined_on_superclass_patched_on_sub(self): self.C.attr = "original" self.patcher.patch_attr(self.D, "attr", "patch") self.assertEquals(self.patcher.get_unpatched_attr(self.D, "attr"), "original") def test_get_unpatched_attr_patched_originally_undefined(self): self.patcher.patch_attr(self.C, "attr", "patch") self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"), Undefined) def test_get_unpatched_attr_patched(self): self.C.attr = "original" self.patcher.patch_attr(self.C, "attr", "patch") self.assertEquals(self.patcher.get_unpatched_attr(self.C, "attr"), "original") def test_get_unpatched_attr_on_instance_originally_undefined(self): self.assertEquals(self.patcher.get_unpatched_attr(self.C(), "attr"), Undefined) def test_get_unpatched_attr_on_instance(self): self.C.attr = "original" self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"), "original") def test_get_unpatched_attr_on_instance_defined_on_superclass(self): self.C.attr = "original" self.patcher.patch_attr(self.C, "attr", "patch") self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"), "original") def test_get_unpatched_attr_on_instance_with_descriptor(self): self.C.attr = property(lambda self: "original") self.patcher.patch_attr(self.C, "attr", "patch") self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"), "original") def test_get_unpatched_attr_on_subclass_with_descriptor(self): calls = [] class Property(object): def __get__(self, obj, cls): calls.append((obj, cls)) return "original" self.C.attr = Property() self.patcher.patch_attr(self.C, "attr", "patch") self.assertEquals(self.patcher.get_unpatched_attr(self.D, "attr"), "original") self.assertEquals(calls, [(None, self.D)]) def test_get_unpatched_attr_on_instance_with_fake_descriptor(self): class BadProperty(object): def __init__(self): # On real, __get__ must be on the class, not on the instance. self.__get__ = lambda self, obj, cls=None: "original" prop = BadProperty() self.C.attr = prop self.patcher.patch_attr(self.C, "attr", "patch") self.assertEquals(self.patcher.get_unpatched_attr(self.D(), "attr"), prop) def test_replay_with_monitored_class(self): self.patcher.monitor(self.C, "call") self.patcher.replay() self.assertEquals(type(self.C.__dict__["__call__"]), PatchedMethod) def test_replay_with_monitored_instance(self): self.patcher.monitor(self.C(), "call") self.patcher.replay() self.assertEquals(type(self.C.__dict__["__call__"]), PatchedMethod) def test_replay_getattr(self): self.patcher.monitor(self.C, "getattr") self.patcher.replay() self.assertEquals(type(self.C.__dict__["__getattribute__"]), PatchedMethod) def test_restore(self): self.patcher.monitor(self.C, "call") self.patcher.replay() self.patcher.restore() self.assertTrue("__call__" not in self.C.__dict__) def test_restore_twice_does_nothing(self): self.patcher.monitor(self.C, "call") self.patcher.replay() self.patcher.restore() self.C.__call__ = "original" self.patcher.restore() self.assertTrue(self.C.__dict__.get("__call__"), "original") def test_patched_call_on_instance(self): self.patcher.monitor(self.C, "call") obj = self.C() obj.__mocker_mock__ = self.MockStub() self.patcher.replay() result = obj(1, a=2) self.assertEquals(result, ("call", (1,), {"a": 2}, obj)) def test_patched_call_on_class(self): self.patcher.monitor(self.C, "call") self.C.__mocker_mock__ = self.MockStub() self.patcher.replay() obj = self.C() result = obj(1, a=2) self.assertEquals(result, ("call", (1,), {"a": 2}, obj)) def test_patched_call_on_class_edge_case(self): """Only "getattr" kind should passthrough on __mocker_* arguments.""" self.patcher.monitor(self.C, "call") self.C.__mocker_mock__ = self.MockStub() self.patcher.replay() obj = self.C() result = obj("__mocker_mock__") self.assertEquals(result, ("call", ("__mocker_mock__",), {}, obj)) def test_patched_getattr_on_class(self): self.patcher.monitor(self.C, "getattr") self.C.__mocker_mock__ = self.MockStub() self.patcher.replay() obj = self.C() result = obj.attr self.assertEquals(result, ("getattr", ("attr",), {}, obj)) def test_patched_getattr_on_unmonitored_object(self): obj1 = self.C() obj1.__mocker_mock__ = self.MockStub() self.patcher.monitor(obj1, "getattr") obj2 = self.C() obj2.attr = "original" self.patcher.replay() self.assertEquals(obj1.attr, ("getattr", ("attr",), {}, obj1)) self.assertEquals(obj2.attr, "original") def test_patched_getattr_on_different_instances(self): def build_getattr(original): def __getattribute__(self, name): if name == "attr": return original return object.__getattribute__(self, name) return __getattribute__ self.C.__getattribute__ = build_getattr("originalC") self.D.__getattribute__ = build_getattr("originalD") class MockStub(object): def __init__(self, id): self.id = id def __mocker_act__(self, kind, args=(), kwargs={}, object=None): return self.id obj1, obj2, obj3, obj4, obj5, obj6 = [self.C() for i in range(6)] obj7, obj8, obj9 = [self.D() for i in range(3)] obj2.__mocker_mock__ = MockStub(2) self.patcher.monitor(obj2, "getattr") obj5.__mocker_mock__ = MockStub(5) self.patcher.monitor(obj5, "getattr") obj8.__mocker_mock__ = MockStub(8) self.patcher.monitor(obj8, "getattr") self.patcher.replay() self.assertEquals(obj1.attr, "originalC") self.assertEquals(obj2.attr, 2) self.assertEquals(obj3.attr, "originalC") self.assertEquals(obj4.attr, "originalC") self.assertEquals(obj5.attr, 5) self.assertEquals(obj6.attr, "originalC") self.assertEquals(obj7.attr, "originalD") self.assertEquals(obj8.attr, 8) self.assertEquals(obj9.attr, "originalD") def test_patched_getattr_execute_getattr(self): class C(object): def __getattribute__(self, attr): if attr == "attr": return "original" action = Action("getattr", ("attr",), {}) obj = C() self.patcher.monitor(obj, "getattr") self.patcher.replay() self.assertEquals(self.patcher.execute(action, obj), "original") def test_execute_getattr_on_unexistent(self): action = Action("getattr", ("attr",), {}) obj = self.C() self.patcher.monitor(obj, "getattr") self.patcher.replay() self.assertRaises(AttributeError, self.patcher.execute, action, obj) def test_patched_real_getattr_on_different_instances(self): def build_getattr(original): def __getattr__(self, name): if name == "attr": return original return object.__getattr__(self, name) return __getattr__ self.C.__getattr__ = build_getattr("originalC") self.D.__getattr__ = build_getattr("originalD") class MockStub(object): def __init__(self, id): self.id = id def __mocker_act__(self, kind, args=(), kwargs={}, object=None): return self.id obj1, obj2, obj3, obj4, obj5, obj6 = [self.C() for i in range(6)] obj7, obj8, obj9 = [self.D() for i in range(3)] obj2.__mocker_mock__ = MockStub(2) self.patcher.monitor(obj2, "getattr") obj5.__mocker_mock__ = MockStub(5) self.patcher.monitor(obj5, "getattr") obj8.__mocker_mock__ = MockStub(8) self.patcher.monitor(obj8, "getattr") self.patcher.replay() self.assertEquals(obj1.attr, "originalC") self.assertEquals(obj2.attr, 2) self.assertEquals(obj3.attr, "originalC") self.assertEquals(obj4.attr, "originalC") self.assertEquals(obj5.attr, 5) self.assertEquals(obj6.attr, "originalC") self.assertEquals(obj7.attr, "originalD") self.assertEquals(obj8.attr, 8) self.assertEquals(obj9.attr, "originalD") def test_patched_real_getattr_execute_getattr(self): class C(object): def __getattr__(self, attr): if attr == "attr": return "original" action = Action("getattr", ("attr",), {}) obj = C() self.patcher.monitor(obj, "getattr") self.patcher.replay() self.assertEquals(self.patcher.execute(action, obj), "original") def test_execute_call(self): class C(object): def __call__(self, *args, **kwargs): return (args, kwargs) action = Action("call", (1,), {"a": 2}) obj = C() self.patcher.monitor(obj, "call") self.patcher.replay() self.assertEquals(self.patcher.execute(action, obj), ((1,), {"a": 2})) def test_recorder_class_getattr(self): self.C.method = lambda: None mock = self.mocker.patch(self.C) mock.method() self.mocker.result("mocked") self.mocker.replay() self.assertEquals(self.C().method(), "mocked") self.assertRaises(AssertionError, self.C().method) def test_recorder_instance_getattr(self): self.C.attr = "original" obj1 = self.C() obj2 = self.C() mock = self.mocker.patch(obj1) mock.attr self.mocker.result("mocked") self.mocker.replay() self.assertEquals(obj1.attr, "mocked") self.assertRaises(AssertionError, getattr, obj1, "attr") self.assertEquals(obj2.attr, "original") self.assertRaises(AttributeError, getattr, obj1, "unexistent") def test_recorder_passthrough(self): class C(object): def __init__(self): self.result = "original" # Value on object's dictionary. def method(self): return self.result mock = self.mocker.patch(C) mock.method() self.mocker.passthrough() self.mocker.replay() obj = C() self.assertEquals(obj.method(), "original") self.assertRaises(AssertionError, obj.method) def test_original_exception_raised(self): class C(object): def use_non_existing_attribute(self): return self.bad_attribute mock = self.mocker.patch(C) mock.any_other_method() self.mocker.replay() obj = C() try: obj.use_non_existing_attribute() except AttributeError, error: message = "'C' object has no attribute 'bad_attribute'" self.assertEquals(message, str(error)) def main(): try: unittest.main() finally: print if coverage: coverage.stop() (filename, executed, missing, missing_human) = coverage.analysis("mocker.py") if missing: print "WARNING: Some statements were not executed:" print coverage.report(["mocker.py"]) else: print "Tests covered 100% of statements!" else: print "WARNING: No coverage test performed (missing coverage.py)" print if __name__ == "__main__": main() mocker-1.0/PKG-INFO0000644000175000017500000000123411407524705014150 0ustar niemeyerniemeyerMetadata-Version: 1.0 Name: mocker Version: 1.0 Summary: Graceful platform for test doubles in Python (mocks, stubs, fakes, and dummies). Home-page: http://labix.org/mocker Author: Gustavo Niemeyer Author-email: gustavo@niemeyer.net License: BSD Download-URL: https://launchpad.net/mocker/+download Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Testing Classifier: Topic :: Software Development :: Libraries :: Python Modules