← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jtv/maas/fakemethod into lp:maas

 

Jeroen T. Vermeulen has proposed merging lp:~jtv/maas/fakemethod into lp:maas.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~jtv/maas/fakemethod/+merge/113505

This is just too handy to keep doing without — I found myself stuck with ugly closures in another branch, trying to kludge around not having it.

I copied the module pretty much verbatim from Launchpad.  Yes, that risks forking.  But we haven't changed it in years, and it's far too small to stand on its own as a project.  (If it were, it would be harder to come to grips with and we might not even bother using it).


Jeroen
-- 
https://code.launchpad.net/~jtv/maas/fakemethod/+merge/113505
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/maas/fakemethod into lp:maas.
=== added file 'src/maastesting/fakemethod.py'
--- src/maastesting/fakemethod.py	1970-01-01 00:00:00 +0000
+++ src/maastesting/fakemethod.py	2012-07-05 07:43:19 +0000
@@ -0,0 +1,72 @@
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+# pylint: disable-msg=E0702
+
+"""Test helper, copied from the Launchpad source tree."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    'FakeMethod',
+    ]
+
+
+class FakeMethod:
+    """Catch any function or method call, and record the fact.
+
+    Use this for easy stubbing.  The call operator can return a fixed
+    value, or raise a fixed exception object.
+
+    This is useful when unit-testing code that does things you don't
+    want to integration-test, e.g. because it wants to talk to remote
+    systems.
+    """
+
+    def __init__(self, result=None, failure=None):
+        """Set up a fake function or method.
+
+        :param result: Value to return.
+        :param failure: Exception to raise.
+        """
+        self.result = result
+        self.failure = failure
+
+        # A log of arguments for each call to this method.
+        self.calls = []
+
+    def __call__(self, *args, **kwargs):
+        """Catch an invocation to the method.
+
+        Increment `call_count`, and adds the arguments to `calls`.
+
+        Accepts any and all parameters.  Raises the failure passed to
+        the constructor, if any; otherwise, returns the result value
+        passed to the constructor.
+        """
+        self.calls.append((args, kwargs))
+
+        if self.failure is None:
+            return self.result
+        else:
+            # pylint thinks this raises None, which is clearly not
+            # possible.  That's why this test disables pylint message
+            # E0702.
+            raise self.failure
+
+    @property
+    def call_count(self):
+        return len(self.calls)
+
+    def extract_args(self):
+        """Return just the calls' positional-arguments tuples."""
+        return [args for args, kwargs in self.calls]
+
+    def extract_kwargs(self):
+        """Return just the calls' keyword-arguments dicts."""
+        return [kwargs for args, kwargs in self.calls]

=== added file 'src/maastesting/tests/test_fakemethod.py'
--- src/maastesting/tests/test_fakemethod.py	1970-01-01 00:00:00 +0000
+++ src/maastesting/tests/test_fakemethod.py	2012-07-05 07:43:19 +0000
@@ -0,0 +1,51 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for :class:`FakeMethod`."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+from maastesting.fakemethod import FakeMethod
+from maastesting.testcase import TestCase
+
+
+class TestFakeMethod(TestCase):
+
+    def test_fakemethod_returns_None_by_default(self):
+        self.assertEqual(None, FakeMethod()())
+
+    def test_fakemethod_returns_given_value(self):
+        self.assertEqual("Input value", FakeMethod("Input value")())
+
+    def test_fakemethod_raises_given_failure(self):
+        class ExpectedException(Exception):
+            pass
+
+        self.assertRaises(
+            ExpectedException,
+            FakeMethod(failure=ExpectedException()))
+
+    def test_fakemethod_has_no_calls_initially(self):
+        self.assertSequenceEqual([], FakeMethod().calls)
+
+    def test_fakemethod_records_call(self):
+        stub = FakeMethod()
+        stub()
+        self.assertSequenceEqual([((), {})], stub.calls)
+
+    def test_fakemethod_records_args(self):
+        stub = FakeMethod()
+        stub(1, 2)
+        self.assertSequenceEqual([((1, 2), {})], stub.calls)
+
+    def test_fakemethod_records_kwargs(self):
+        stub = FakeMethod()
+        stub(x=10)
+        self.assertSequenceEqual([((), {'x': 10})], stub.calls)