diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 7fdea58..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.pyc -*.egg-info diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 414bf35..0000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2011 Hatem Nassrat - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.rst b/README.rst index 8386c1b..8c33253 100644 --- a/README.rst +++ b/README.rst @@ -1,59 +1,3 @@ -MiniMockTest -============ +This branch contains the gh-page for MiniMockTest. -.. image:: https://img.shields.io/pypi/v/MiniMockTest.svg - :target: https://crate.io/packages/MiniMockTest/#info - -.. image:: https://img.shields.io/pypi/dm/MiniMockTest.svg - :target: https://crate.io/packages/MiniMockTest/#info - -.. image:: https://requires.io/github/pykler/MiniMockTest/requirements.png?branch=master - :target: https://requires.io/github/pykler/MiniMockTest/requirements/?branch=master - -Documentation -------------- - -This module provides a class which extends unittest.TestCase which can -be used to easily mock objects in a test case. This module can be used -in conjunction with other test case classes by means of multiple -inheritance http://docs.python.org/tutorial/classes.html#multiple-inheritance - -An example of using Django's test case with this module's test case is -simply as follows:: - - from minimocktest import MockTestCase - from django.test import TestCase - from django.test.client import Client - - class DjangoTestCase(TestCase, MockTestCase): - ''' - A TestCase class that combines minimocktest and django.test.TestCase - ''' - - def _pre_setup(self): - TestCase._pre_setup(self) - MockTestCase.setUp(self) - # optional: shortcut client handle for quick testing - self.client = Client() - - def _post_teardown(self): - MockTestCase.tearDown(self) - TestCase._post_teardown(self) - - def setUp(self): - pass - - def tearDown(self): - pass - - -COPYING -------- - -This module is provided under the terms of the MIT Licence. The code in -this module relies on the "minimock" library which is included as part of -the package. The copyright notice for "minimock" is included at the -beginning of its source code file. - -For more information, see the file LICENCSE or -http://www.opensource.org/licenses/mit-license.php +All files and content in this branch fall under the same licencse as MiniMockTest itself. diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..6d67c09 --- /dev/null +++ b/_config.yml @@ -0,0 +1,2 @@ +markdown: rdiscount +pygments: true diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 0000000..ab71b12 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,31 @@ + + + + + + {{ page.title }} + + + + + + + + + Fork me on GitHub + +
+ {{ content }} +
+ + + + + diff --git a/css/screen.css b/css/screen.css new file mode 100644 index 0000000..defb5c9 --- /dev/null +++ b/css/screen.css @@ -0,0 +1,21 @@ +body { + margin-top: 1.0em; + background-color: #ffcc22; + font-family: Helvetica, Arial, FreeSans, san-serif; + color: #000000; +} +#container { + margin: 0 auto; + width: 750px; +} +h1 { font-size: 3.8em; color: #0033dd; margin-bottom: 3px; } +h1 .small { font-size: 0.4em; } +h1 a { text-decoration: none } +h2 { font-size: 1.5em; color: #0033dd; } +h3 { text-align: center; color: #0033dd; } +a { color: #0033dd; } +.description { font-size: 1.2em; margin-bottom: 30px; margin-top: 30px; font-style: italic;} +.download { float: right; } +pre { background: #000; color: #fff; padding: 15px;} +hr { border: 0; width: 80%; border-bottom: 1px solid #aaa} +.footer { text-align:center; padding-top:30px; font-style: italic; } diff --git a/css/syntax.css b/css/syntax.css new file mode 100644 index 0000000..2774b76 --- /dev/null +++ b/css/syntax.css @@ -0,0 +1,60 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #aaaaaa } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #000080 } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ diff --git a/index.html b/index.html new file mode 100644 index 0000000..ea84bcd --- /dev/null +++ b/index.html @@ -0,0 +1,124 @@ +--- +layout: default +title: MiniMockTest +--- + +
+ + + + +
+ +

MiniMockTest + by pykler

+ +
+ Python unittest TestCase that wraps minimock for easier use +
+ +

minimocktest.MockTestCase provides a full featured unittest.TestCase subclass that allows for mocking. Using this class as the basis of your unittests allows you to mock functions by calling + +{% highlight python %} +self.mock('urllib2.urlopen', returns=StringIO.StringIO('{"error": 1}')) +{% endhighlight %} + +or creating mock objects + +{% highlight python %} +someobj = self.Mock('someobj') +{% endhighlight %} + +Also you do not have to worry about resetting the mock, all that is taken care of by the MockTestCase tearDown function.

+ +

Examples

+ +

Simple Testing

+ +{% highlight python %} +import unittest +from minimocktest import MockTestCase + +import StringIO +import urllib2 + +def urldump(url): + ''' simple function to be tested ''' + f = urllib2.urlopen(url) + try: + return f.read() + finally: + f.close() + +class MySimpleTestCase(MockTestCase): + def setUp(self): + super(self.__class__, self).setUp() + self.file = StringIO.StringIO('MiniMockTest') + self.file.close = self.Mock('file_close_function') + def test_urldump_dumpsContentProperly(self): + self.mock('urllib2.urlopen', returns=self.file) + self.assertEquals(urldump('http://pykler.github.com/'), 'MiniMockTest') + self.assertSameTrace('\n'.join([ + "Called urllib2.urlopen('http://pykler.github.com/')", + "Called file_close_function()", + ])) + urllib2.urlopen('anything') + self.mock('urllib2.urlopen', returns=self.file, tracker=None) + urllib2.urlopen('this is not tracked') + self.assertTrace("Called urllib2.urlopen('anything')") + self.assertTrace("Called urllib2.urlopen('this is not tracked')", includes=False) + self.assertSameTrace('\n'.join([ + "Called urllib2.urlopen('http://pykler.github.com/')", + "Called file_close_function()", + "Called urllib2.urlopen('anything')", + ])) + +suite = unittest.TestLoader().loadTestsFromTestCase(MySimpleTestCase) +unittest.TextTestRunner().run(suite) +{% endhighlight %} + +

Django Integration

+ +To integrate with Django, simply create the following class, and use it as your base instead of using the django.test.TestCase directly. + +{% highlight python %} +from minimocktest import MockTestCase +from django.test import TestCase +from django.test.client import Client + +class DjangoTestCase(TestCase, MockTestCase): + ''' + A TestCase class that combines minimocktest and django.test.TestCase + ''' + def _pre_setup(self): + TestCase._pre_setup(self) + MockTestCase.setUp(self) + # optional: shortcut client handle for quick testing + self.client = Client() + + def _post_teardown(self): + MockTestCase.tearDown(self) + TestCase._post_teardown(self) + + def setUp(self): + pass + + def tearDown(self): + pass +{% endhighlight %} + + +

Download

+

+ You can download this project in either + zip or + tar formats. +

+

You can also clone the project with Git + by running: +

$ git clone git://github.com/pykler/MiniMockTest
+

+ + diff --git a/minimocktest/__init__.py b/minimocktest/__init__.py deleted file mode 100644 index 0acbb49..0000000 --- a/minimocktest/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ -# (c) 2011 Hatem Nassrat -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -''' -MiniMockTest module, holds MockTestCase class for easy to use mocking in testcases -''' - -import __builtin__ -import inspect -from unittest import TestCase -import minimock - -class MockTestCase(TestCase): - ''' - A TestCase class that integrates minimock functionailty - `self.tt` minimock tracker object - `self.mock` calls minimock.mock using tracker=`self.tt` - `self.assertSameTrace` calls minimock.assert_same_trace with `self.tt` - ''' - - def setUp(self): - TestCase.setUp(self) - self.tt = minimock.TraceTracker() - - def tearDown(self): - self.mockRestore() - TestCase.tearDown(self) - - def Mock(self, *args, **kwargs): - if 'tracker' not in kwargs: - kwargs['tracker'] = self.tt - return minimock.Mock(*args, **kwargs) - - def mock(self, *args, **kwargs): - if len(args) <= 1 and 'nsdicts' not in kwargs: - # custom nsdicts not used, inspect caller's stack - stack = inspect.stack() - try: - # stack[1][0] is the frame object of the caller to this function - globals_ = stack[1][0].f_globals - locals_ = stack[1][0].f_locals - nsdicts = (locals_, globals_, __builtin__.__dict__) - finally: - del(stack) - kwargs['nsdicts'] = nsdicts - if 'tracker' not in kwargs: - kwargs['tracker'] = self.tt - return minimock.mock(*args, **kwargs) - - def mockRestore(self): - minimock.restore() - - def assertSameTrace(self, want): - ''' - Asserts if want == trace - ''' - minimock.assert_same_trace(self.tt, want) - - def assertTrace(self, want, includes=True): - ''' - Asserts if want is included in trace, or excluded when includes=False - ''' - trace = self.tt.dump() - condition = want in trace - fragment = 'not in' - if not includes: - condition = not condition - fragment = 'in' - self.assertTrue(condition, '%s %s trace:\n%s' %(want, fragment, trace)) diff --git a/minimocktest/minimock.py b/minimocktest/minimock.py deleted file mode 100644 index ebd1500..0000000 --- a/minimocktest/minimock.py +++ /dev/null @@ -1,644 +0,0 @@ -# (c) 2006-2009 Ian Bicking, Mike Beachy, and contributors -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -r""" -minimock is a simple library for doing Mock objects with doctest. -When using doctest, mock objects can be very simple. - -Here's an example of something we might test, a simple email sender:: - - >>> import smtplib - >>> def send_email(from_addr, to_addr, subject, body): - ... conn = smtplib.SMTP('localhost') - ... msg = 'To: %s\nFrom: %s\nSubject: %s\n\n%s' % ( - ... to_addr, from_addr, subject, body) - ... conn.sendmail(from_addr, [to_addr], msg) - ... conn.quit() - -Now we want to make a mock ``smtplib.SMTP`` object. We'll have to -inject our mock into the ``smtplib`` module:: - - >>> smtplib.SMTP = Mock('smtplib.SMTP') - >>> smtplib.SMTP.mock_returns = Mock('smtp_connection') - -Now we do the test:: - - >>> send_email('ianb@colorstudy.com', 'joe@example.com', - ... 'Hi there!', 'How is it going?') - Called smtplib.SMTP('localhost') - Called smtp_connection.sendmail( - 'ianb@colorstudy.com', - ['joe@example.com'], - 'To: joe@example.com\nFrom: ianb@colorstudy.com\nSubject: Hi there!\n\nHow is it going?') - Called smtp_connection.quit() - -Voila! We've tested implicitly that no unexpected methods were called -on the object. We've also tested the arguments that the mock object -got. We've provided fake return calls (for the ``smtplib.SMTP()`` -constructor). These are all the core parts of a mock library. The -implementation is simple because most of the work is done by doctest. -""" - -__all__ = ["mock", "restore", "Mock", "TraceTracker", "assert_same_trace"] - -import sys -import inspect -import doctest -import re -import textwrap - -try: - import builtins -except ImportError: - # python < 3 - import __builtin__ as builtins - try: - from cStringIO import StringIO - except ImportError: - from StringIO import StringIO -else: - # python 3 - from io import StringIO -try: - next -except NameError: - # python < 2.6 - def next(x): - return x.next() - -# A list of mocked objects. Each item is a tuple of (original object, -# namespace dict, object name, and a list of object attributes). -# -mocked = [] - -def lookup_by_name(name, nsdicts): - """ - Look up an object by name from a sequence of namespace dictionaries. - Returns a tuple of (nsdict, obj_name, attrs); nsdict is the namespace - dictionary the name was found in, obj_name is the name of the base object - the name is bound to, and the attrs list is the chain of attributes - of the object that completes the name. - - >>> import os - >>> nsdict, obj_name, attrs = lookup_by_name("os.path.isdir", - ... (locals(),)) - >>> obj_name, attrs - ('os', ['path', 'isdir']) - >>> getattr(getattr(nsdict[obj_name], attrs[0]), attrs[1]) # doctest: +ELLIPSIS - - >>> lookup_by_name("os.monkey", (locals(),)) - Traceback (most recent call last): - ... - NameError: name 'os.monkey' is not defined - - """ - for nsdict in nsdicts: - attrs = name.split(".") - names = [] - - while attrs: - names.append(attrs.pop(0)) - obj_name = ".".join(names) - - if obj_name in nsdict: - attr_copy = attrs[:] - tmp = nsdict[obj_name] - try: - while attr_copy: - tmp = getattr(tmp, attr_copy.pop(0)) - except AttributeError: - pass - else: - return nsdict, obj_name, attrs - - raise NameError("name '%s' is not defined" % name) - -def mock(name, nsdicts=None, mock_obj=None, **kw): - """ - Mock the named object, placing a Mock instance in the correct namespace - dictionary. If no iterable of namespace dicts is provided, use - introspection to get the locals and globals of the caller of this - function, as well as __builtin__.__dict__ to allow mocking built-ins. - - All additional keyword args are passed on to the Mock object - initializer. - - An example of how os.path.isfile is replaced:: - - >>> import os - >>> os.path.isfile # doctest: +ELLIPSIS - - >>> isfile_id = id(os.path.isfile) - >>> mock("os.path.isfile", returns=True) - >>> os.path.isfile # doctest: +ELLIPSIS - - >>> os.path.isfile("/foo/bar/baz") - Called os.path.isfile('/foo/bar/baz') - True - >>> mock_id = id(os.path.isfile) - >>> mock_id != isfile_id - True - - A second mock object will replace the first, but the original object - will be the one replaced with the replace() function. - - >>> mock("os.path.isfile", returns=False) - >>> mock_id != id(os.path.isfile) - True - >>> restore() - >>> os.path.isfile # doctest: +ELLIPSIS - - >>> isfile_id == id(os.path.isfile) - True - - Test mocking a built-in function:: - - >>> try: - ... input = raw_input - ... except NameError: - ... pass # for Python 3 - >>> mock("input", returns="okay") - >>> input() - Called input() - 'okay' - >>> restore() - - Test mocking and restoring a classmethod and staticmethod:: - - >>> class Test(object): - ... @classmethod - ... def cm(cls): - ... return 'cm' - ... @staticmethod - ... def sm(): - ... return 'sm' - >>> mock('Test.cm', returns='mocked') - >>> mock('Test.sm', returns='mocked') - >>> Test.cm() - Called Test.cm() - 'mocked' - >>> Test.sm() - Called Test.sm() - 'mocked' - >>> restore() - >>> Test.cm() - 'cm' - >>> Test.sm() - 'sm' - - Test mocking a proxy object:: - - >>> class Proxy(object): - ... def __init__(self, obj): - ... self._obj = obj - ... def __getattr__(self, name): - ... return getattr(self._obj, name) - >>> import os - >>> os = Proxy(os) - >>> os.path.isfile # doctest: +ELLIPSIS - - >>> mock('os.path.isfile') - >>> os.path.isfile # doctest: +ELLIPSIS - - >>> restore() - - """ - if nsdicts is None: - stack = inspect.stack() - try: - # stack[1][0] is the frame object of the caller to this function - globals_ = stack[1][0].f_globals - locals_ = stack[1][0].f_locals - nsdicts = (locals_, globals_, builtins.__dict__) - finally: - del(stack) - - if mock_obj is None: - mock_obj = Mock(name, **kw) - - nsdict, obj_name, attrs = lookup_by_name(name, nsdicts) - - # Get the original object and replace it with the mock object. - tmp = nsdict[obj_name] - - # if run from a doctest, nsdict may point to a *copy* of the - # global namespace, so instead use tmp.func_globals if present. - # we use isinstance(gettattr(...), dict) rather than hasattr - # because if tmp is itself a mock object, tmp.func_globals will - # return another mock object - if isinstance(getattr(tmp, 'func_globals', None), dict): - nsdict = tmp.__globals__ - if not attrs: - original = tmp - nsdict[obj_name] = mock_obj - else: - for attr in attrs[:-1]: - try: - tmp = tmp.__dict__[attr] - except KeyError: - tmp = getattr(tmp, attr) - try: - original = tmp.__dict__[attrs[-1]] - except KeyError: - original = getattr(tmp, attrs[-1]) - setattr(tmp, attrs[-1], mock_obj) - - mocked.append((original, nsdict, obj_name, attrs)) - -def restore(): - """ - Restore all mocked objects. - """ - global mocked - - # Restore the objects in the reverse order of their mocking to assure - # the original state is retrieved. - while mocked: - original, nsdict, name, attrs = mocked.pop() - if not attrs: - nsdict[name] = original - else: - tmp = nsdict[name] - for attr in attrs[:-1]: - try: - tmp = tmp.__dict__[attr] - except KeyError: - tmp = getattr(tmp, attr) - setattr(tmp, attrs[-1], original) - -def assert_same_trace(tracker, want): - r""" - Check that the mock objects using ``tracker`` have been used as expected. - - :param tracker: a :class:`TraceTracker` instance - :param want: the expected :class:`Printer` output - :type want: string - :raises: :exc:`AssertionError` if the expected and observed outputs don't - match - - Example:: - - >>> tt = TraceTracker() - >>> m = Mock('mock_obj', tracker=tt) - >>> m.some_meth('dummy argument') - >>> assert_same_trace(tt, - ... "Called mock_obj.some_meth('dummy argument')\n") - >>> assert_same_trace(tt, "Non-matching trace") # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - AssertionError... - """ - assert tracker.check(want), tracker.diff(want) - -class AbstractTracker(object): - def __init__(self, *args, **kw): - raise NotImplementedError - - def call(self, *args, **kw): - raise NotImplementedError - - def set(self, *args, **kw): - raise NotImplementedError - -class Printer(AbstractTracker): - """Prints all calls to the file it's instantiated with. - Can take any object that implements `write'. - """ - def __init__(self, file): - self.file = file - - def call(self, func_name, *args, **kw): - parts = [repr(a) for a in args] - parts.extend( - '%s=%r' % (items) for items in sorted(kw.items())) - msg = 'Called %s(%s)\n' % (func_name, ', '.join(parts)) - if len(msg) > 80: - msg = 'Called %s(\n %s)\n' % ( - func_name, ',\n '.join(parts)) - self.file.write(msg) - - def set(self, obj_name, attr, value): - """ - >>> z = Mock('z', show_attrs=True) - >>> z.a = 2 - Set z.a = 2 - """ - self.file.write('Set %s.%s = %r\n' % (obj_name, attr, value)) - -class TraceTracker(Printer): - """ - :class:`AbstractTracker` implementation for using MiniMock in non- - :mod:`doctest` tests. Follows the pattern of recording minimocked - object usage as strings, then using the facilities of :mod:`doctest` - to assert the correctness of these usage strings. - """ - def __init__(self, *args, **kw): - self.out = StringIO() - super(TraceTracker, self).__init__(self.out, *args, **kw) - self.checker = MinimockOutputChecker() - self.options = doctest.ELLIPSIS - self.options |= doctest.NORMALIZE_INDENTATION - self.options |= doctest.NORMALIZE_FUNCTION_PARAMETERS - self.options |= doctest.REPORT_UDIFF - - def check(self, want): - r""" - Compare observed MiniMock usage with that which we expected. - - :param want: the :class:`Printer` output that results from expected - usage of mocked objects - :type want: string - :rtype: a ``True`` value if the check passed, ``False`` otherwise - - Example:: - - >>> tt = TraceTracker() - >>> m = Mock('mock_obj', tracker=tt) - >>> m.some_meth('arg1') - >>> tt.check("Called mock_obj.some_meth('arg1')") - True - >>> tt.clear() - >>> m.some_meth('arg2') - >>> tt.check("does not match") - False - """ - return self.checker.check_output(want, self.dump(), - optionflags=self.options) - - def diff(self, want): - r""" - Analyse differences between observed MiniMock usage and that which - we expected, if any. - - :param want: the :class:`Printer` output that results from expected - usage of mocked objects - :type want: string - :rtype: a string summary of differences between the observed usage and - the ``want`` parameter - - Example:: - - >>> tt = TraceTracker() - >>> m = Mock('mock_obj', tracker=tt) - >>> m.some_meth('dummy argument') - >>> tt.diff("does not match") - "Expected:\n does not match\nGot:\n Called mock_obj.some_meth('dummy argument')\n" - >>> tt.diff("Called mock_obj.some_meth('dummy argument')") - '' - """ - if self.check(want): - # doctest's output_difference always returns a diff, even if - # there's no difference: short circuit that feature. - return '' - else: - return self.checker.output_difference(doctest.Example("", want), - self.dump(), optionflags=self.options) - - def dump(self): - r""" - Return the MiniMock object usage so far. - - Example:: - - >>> tt = TraceTracker() - >>> m = Mock('mock_obj', tracker=tt) - >>> m.some_meth('dummy argument') - >>> tt.dump() - "Called mock_obj.some_meth('dummy argument')\n" - """ - return self.out.getvalue() - - def clear(self): - """Clear the MiniMock object usage that has been tracked so far. - - truncate() was modified not to change the file position anymore - in Python 3.1.2, so should be sought explicitly. - http://bugs.python.org/issue8558 - """ - self.out.truncate(0) - self.out.seek(0) - - -def normalize_function_parameters(text): - r""" - Return a version of ``text`` with function parameters normalized. - - The normalisations performed are: - - * Remove any whitespace sequence between an opening - parenthesis '(' and a subsequent non-whitespace character. - - * Remove any whitespace sequence between a non-whitespace - character and a closing parenthesis ')'. - - * Ensure a comma ',' and a subsequent non-whitespace character - are separated by a single space ' '. - - Example:: - - >>> tt = TraceTracker() - >>> foo = Mock("foo", tracker=tt) - >>> expect_mock_output = '''\ - ... Called foo.bar('baz') - ... ''' - >>> foo.bar('baz') - >>> tt.check(expect_mock_output) - True - >>> tt.clear() - >>> expect_mock_output = '''\ - ... Called foo.bar( - ... 'baz') - ... ''' - >>> foo.bar('baz') - >>> tt.check(expect_mock_output) - True - """ - normalized_text = text - normalize_map = { - re.compile(r"\(\s+(\S)"): r"(\1", - re.compile(r"(\S)\s+\)"): r"\1)", - re.compile(r",\s*(\S)"): r", \1", - } - for search_pattern, replace_pattern in normalize_map.items(): - normalized_text = re.sub( - search_pattern, replace_pattern, normalized_text) - - return normalized_text - - -doctest.NORMALIZE_INDENTATION = ( - doctest.register_optionflag('NORMALIZE_INDENTATION')) -doctest.NORMALIZE_FUNCTION_PARAMETERS = ( - doctest.register_optionflag('NORMALIZE_FUNCTION_PARAMETERS')) - - -class MinimockOutputChecker(doctest.OutputChecker, object): - """Class for matching output of MiniMock objects against expectations. - """ - - def check_output(self, want, got, optionflags): - if (optionflags & doctest.NORMALIZE_INDENTATION): - want = textwrap.dedent(want).rstrip() - got = textwrap.dedent(got).rstrip() - if (optionflags & doctest.NORMALIZE_FUNCTION_PARAMETERS): - want = normalize_function_parameters(want) - got = normalize_function_parameters(got) - output_match = super(MinimockOutputChecker, self).check_output( - want, got, optionflags) - return output_match - check_output.__doc__ = doctest.OutputChecker.check_output.__doc__ - - -class _DefaultTracker(object): - def __repr__(self): - return '(default tracker)' -DefaultTracker = _DefaultTracker() -del _DefaultTracker - -class Mock(object): - - def __init__(self, name, returns=None, returns_iter=None, - returns_func=None, raises=None, show_attrs=False, - tracker=DefaultTracker, **kw): - _obsetattr = object.__setattr__ - _obsetattr(self, 'mock_name', name) - _obsetattr(self, 'mock_returns', returns) - if returns_iter is not None: - returns_iter = iter(returns_iter) - _obsetattr(self, 'mock_returns_iter', returns_iter) - _obsetattr(self, 'mock_returns_func', returns_func) - _obsetattr(self, 'mock_raises', raises) - _obsetattr(self, 'mock_attrs', kw) - _obsetattr(self, 'mock_show_attrs', show_attrs) - if tracker is DefaultTracker: - tracker = Printer(sys.stdout) - _obsetattr(self, 'mock_tracker', tracker) - - def __repr__(self): - return '' % (hex(id(self)), self.mock_name) - - def __call__(self, *args, **kw): - if self.mock_tracker is not None: - self.mock_tracker.call(self.mock_name, *args, **kw) - return self._mock_return(*args, **kw) - - def _mock_return(self, *args, **kw): - if self.mock_raises is not None: - raise self.mock_raises - elif self.mock_returns is not None: - return self.mock_returns - elif self.mock_returns_iter is not None: - try: - return next(self.mock_returns_iter) - except StopIteration: - raise Exception("No more mock return values are present.") - elif self.mock_returns_func is not None: - return self.mock_returns_func(*args, **kw) - else: - return None - - def __getattr__(self, attr): - if attr not in self.mock_attrs: - if self.mock_name: - new_name = self.mock_name + '.' + attr - else: - new_name = attr - self.mock_attrs[attr] = Mock(new_name, - show_attrs=self.mock_show_attrs, - tracker=self.mock_tracker) - return self.mock_attrs[attr] - - def __setattr__(self, attr, value): - if attr in frozenset(( - 'mock_raises', - 'mock_returns', - 'mock_returns_func', - 'mock_returns_iter', - 'mock_tracker', - 'mock_show_attrs', - )): - if attr == 'mock_returns_iter' and value is not None: - value = iter(value) - object.__setattr__(self, attr, value) - else: - if self.mock_show_attrs and self.mock_tracker is not None: - self.mock_tracker.set(self.mock_name, attr, value) - self.mock_attrs[attr] = value - -__test__ = { - "Mock" : - r""" - Test setting various "mock_" attributes on an existing Mock object. - - >>> m = Mock('mock_obj', tracker=None) - >>> m.mock_returns = 42 - >>> m() - 42 - >>> m.mock_returns = None - >>> m.mock_returns_func = lambda x: x*x - >>> m(3) - 9 - >>> m.mock_returns_func = None - >>> m.mock_returns_iter = [True, False] - >>> m() - True - >>> m() - False - >>> m.mock_returns_iter = None - >>> m.mock_raises = ValueError - >>> try: - ... m() - ... except ValueError: - ... pass - ... else: - ... raise AssertionError('m() should have raised ValueError') - >>> m.mock_tracker = Printer(sys.stdout) - >>> m.mock_show_attrs = True - >>> m.a = 2 - Set mock_obj.a = 2 - """, - - "mock" : - r""" - An additional test for mocking a function accessed directly (i.e. - not via object attributes). - - >>> import os - >>> rename = os.rename - >>> orig_id = id(rename) - >>> mock("rename") - >>> mock_id = id(rename) - >>> mock("rename") - >>> mock_id != id(rename) - True - >>> restore() - >>> orig_id == id(rename) == id(os.rename) - True - - The example from the module docstring, done with the mock/restore - functions. - - >>> import smtplib - >>> def send_email(from_addr, to_addr, subject, body): - ... conn = smtplib.SMTP('localhost') - ... msg = 'To: %s\nFrom: %s\nSubject: %s\n\n%s' % ( - ... to_addr, from_addr, subject, body) - ... conn.sendmail(from_addr, [to_addr], msg) - ... conn.quit() - - >>> mock("smtplib.SMTP", returns=Mock('smtp_connection')) - >>> send_email('ianb@colorstudy.com', 'joe@example.com', - ... 'Hi there!', 'How is it going?') - Called smtplib.SMTP('localhost') - Called smtp_connection.sendmail( - 'ianb@colorstudy.com', - ['joe@example.com'], - 'To: joe@example.com\nFrom: ianb@colorstudy.com\nSubject: Hi there!\n\nHow is it going?') - Called smtp_connection.quit() - >>> restore() - - """, -} - -if __name__ == '__main__': - import doctest - doctest.testmod(optionflags=doctest.ELLIPSIS) diff --git a/setup.py b/setup.py deleted file mode 100644 index f15656f..0000000 --- a/setup.py +++ /dev/null @@ -1,24 +0,0 @@ -from setuptools import setup, find_packages -import sys, os - -setup(name='MiniMockTest', - version='0.5', - description="Custom unittest TestCase that wraps minimock", - long_description="""\ -Custom unittest TestCase that wraps minimock for easier use """, - classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers - keywords='minimock mock mocking test unittest TestCase MockTestCase', - author='Hatem Nassrat', - author_email='hnassrat@gmail.com', - url='http://pykler.github.com/MiniMockTest/', - license='MIT', - packages=find_packages(exclude=['ez_setup']), - include_package_data=True, - zip_safe=True, - install_requires=[ - # -*- Extra requirements: -*- - ], - entry_points=""" - # -*- Entry points: -*- - """, - )