summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsmiddlek <smiddlek@b1010a0a-674b-0410-b734-77272b80c875>2010-03-18 17:04:35 +0000
committersmiddlek <smiddlek@b1010a0a-674b-0410-b734-77272b80c875>2010-03-18 17:04:35 +0000
commit33f2e36889a478ee5c26a56625f8505c72a9772d (patch)
tree7a7de76107fe914cfd47ac667d8ff4ce9608fed6
parent8d018291d83381d57fe40305ee504861d44f8bcc (diff)
downloadmox-33f2e36889a478ee5c26a56625f8505c72a9772d.tar.gz
Thanks to matthew.blain@google.com for ContainsAttributeValue comparator
and Adeodato Simo for adding stubs to the MoxTestBase and fixing up some of the tests. git-svn-id: http://pymox.googlecode.com/svn/trunk@40 b1010a0a-674b-0410-b734-77272b80c875
-rwxr-xr-xmox.py64
-rwxr-xr-xmox_test.py153
-rwxr-xr-xmox_test_helper.py19
3 files changed, 220 insertions, 16 deletions
diff --git a/mox.py b/mox.py
index 593163a..ba1e095 100755
--- a/mox.py
+++ b/mox.py
@@ -430,6 +430,16 @@ class MockObject(MockAnything, object):
self._known_methods = set()
self._known_vars = set()
self._class_to_mock = class_to_mock
+ try:
+ self._description = class_to_mock.__name__
+ # If class_to_mock is a mock itself, then we'll get an UnknownMethodCall
+ # error here from the underlying call to __getattr__('__name__')
+ except (UnknownMethodCallError, AttributeError):
+ try:
+ self._description = type(class_to_mock).__name__
+ except AttributeError:
+ pass
+
for method in dir(class_to_mock):
if callable(getattr(class_to_mock, method)):
self._known_methods.add(method)
@@ -761,6 +771,7 @@ class MockMethod(object):
"""
self._name = method_name
+ self.__name__ = method_name
self._call_queue = call_queue
if not isinstance(call_queue, deque):
self._call_queue = deque(self._call_queue)
@@ -803,7 +814,9 @@ class MockMethod(object):
expected_method = self._VerifyMethodCall()
if expected_method._side_effects:
- expected_method._side_effects(*params, **named_params)
+ result = expected_method._side_effects(*params, **named_params)
+ if expected_method._return_value is None:
+ expected_method._return_value = result
if expected_method._exception:
raise expected_method._exception
@@ -1286,6 +1299,37 @@ class ContainsKeyValue(Comparator):
return '<map containing the entry \'%s: %s\'>' % (self._key, self._value)
+class ContainsAttributeValue(Comparator):
+ """Checks whether a passed parameter contains attributes with a given value.
+
+ Example:
+ mock_dao.UpdateSomething(ContainsAttribute('stevepm', stevepm_user_info))
+ """
+
+ def __init__(self, key, value):
+ """Initialize.
+
+ Args:
+ # key: an attribute name of an object
+ # value: the corresponding value
+ """
+
+ self._key = key
+ self._value = value
+
+ def equals(self, rhs):
+ """Check whether the given attribute has a matching value in the rhs object.
+
+ Returns:
+ bool
+ """
+
+ try:
+ return getattr(rhs, self._key) == self._value
+ except Exception:
+ return False
+
+
class SameElementsAs(Comparator):
"""Checks whether iterables contain the same elements (ignoring order).
@@ -1621,7 +1665,8 @@ class MoxMetaTestBase(type):
# for a case when test class is not the immediate child of MoxTestBase
for base in bases:
for attr_name in dir(base):
- d[attr_name] = getattr(base, attr_name)
+ if attr_name not in d:
+ d[attr_name] = getattr(base, attr_name)
for func_name, func in d.items():
if func_name.startswith('test') and callable(func):
@@ -1643,14 +1688,21 @@ class MoxMetaTestBase(type):
"""
def new_method(self, *args, **kwargs):
mox_obj = getattr(self, 'mox', None)
+ stubout_obj = getattr(self, 'stubs', None)
cleanup_mox = False
+ cleanup_stubout = False
if mox_obj and isinstance(mox_obj, Mox):
cleanup_mox = True
+ if stubout_obj and isinstance(stubout_obj, stubout.StubOutForTesting):
+ cleanup_stubout = True
try:
func(self, *args, **kwargs)
finally:
if cleanup_mox:
mox_obj.UnsetStubs()
+ if cleanup_stubout:
+ stubout_obj.UnsetAll()
+ stubout_obj.SmartUnsetAll()
if cleanup_mox:
mox_obj.VerifyAll()
new_method.__name__ = func.__name__
@@ -1662,9 +1714,10 @@ class MoxMetaTestBase(type):
class MoxTestBase(unittest.TestCase):
"""Convenience test class to make stubbing easier.
- Sets up a "mox" attribute which is an instance of Mox - any mox tests will
- want this. Also automatically unsets any stubs and verifies that all mock
- methods have been called at the end of each test, eliminating boilerplate
+ Sets up a "mox" attribute which is an instance of Mox (any mox tests will
+ want this), and a "stubs" attribute that is an instance of StubOutForTesting
+ (needed at times). Also automatically unsets any stubs and verifies that all
+ mock methods have been called at the end of each test, eliminating boilerplate
code.
"""
@@ -1673,3 +1726,4 @@ class MoxTestBase(unittest.TestCase):
def setUp(self):
super(MoxTestBase, self).setUp()
self.mox = Mox()
+ self.stubs = stubout.StubOutForTesting()
diff --git a/mox_test.py b/mox_test.py
index e1626d6..ea12176 100755
--- a/mox_test.py
+++ b/mox_test.py
@@ -24,6 +24,7 @@ import mox
import mox_test_helper
+OS_LISTDIR = mox_test_helper.os.listdir
class ExpectedMethodCallsErrorTest(unittest.TestCase):
"""Test creation and string conversion of ExpectedMethodCallsError."""
@@ -147,6 +148,32 @@ class ContainsKeyValueTest(unittest.TestCase):
self.failIf(mox.ContainsKeyValue("qux", 1) == {"key": 2})
+class ContainsAttributeValueTest(unittest.TestCase):
+ """Test ContainsAttributeValue correctly identifies properties in an object.
+ """
+
+ def setUp(self):
+ """Create an object to test with."""
+
+
+ class TestObject(object):
+ key = 1
+
+ self.test_object = TestObject()
+
+ def testValidPair(self):
+ """Should return True if the object has the key attribute and it matches."""
+ self.assert_(mox.ContainsAttributeValue("key", 1) == self.test_object)
+
+ def testInvalidValue(self):
+ """Should return False if the value is not correct."""
+ self.failIf(mox.ContainsKeyValue("key", 2) == self.test_object)
+
+ def testInvalidKey(self):
+ """Should return False if they the object doesn't have the property."""
+ self.failIf(mox.ContainsKeyValue("qux", 1) == self.test_object)
+
+
class InTest(unittest.TestCase):
"""Test In correctly identifies a key in a list/dict"""
@@ -302,6 +329,10 @@ class MockMethodTest(unittest.TestCase):
self.mock_method = mox.MockMethod("testMethod", [self.expected_method],
True)
+ def testNameAttribute(self):
+ """Should provide a __name__ attribute."""
+ self.assertEquals('testMethod', self.mock_method.__name__)
+
def testAndReturnNoneByDefault(self):
"""Should return None by default."""
return_value = self.mock_method(['original'])
@@ -330,6 +361,34 @@ class MockMethodTest(unittest.TestCase):
self.mock_method(local_list)
self.assertEquals('mutation', local_list[0])
+ def testWithReturningSideEffects(self):
+ """Should call state modifier and propagate its return value."""
+ local_list = ['original']
+ expected_return = 'expected_return'
+ def modifier_with_return(mutable_list):
+ self.assertTrue(local_list is mutable_list)
+ mutable_list[0] = 'mutation'
+ return expected_return
+ self.expected_method.WithSideEffects(modifier_with_return)
+ actual_return = self.mock_method(local_list)
+ self.assertEquals('mutation', local_list[0])
+ self.assertEquals(expected_return, actual_return)
+
+ def testWithReturningSideEffectsWithAndReturn(self):
+ """Should call state modifier and ignore its return value."""
+ local_list = ['original']
+ expected_return = 'expected_return'
+ unexpected_return = 'unexpected_return'
+ def modifier_with_return(mutable_list):
+ self.assertTrue(local_list is mutable_list)
+ mutable_list[0] = 'mutation'
+ return unexpected_return
+ self.expected_method.WithSideEffects(modifier_with_return).AndReturn(
+ expected_return)
+ actual_return = self.mock_method(local_list)
+ self.assertEquals('mutation', local_list[0])
+ self.assertEquals(expected_return, actual_return)
+
def testEqualityNoParamsEqual(self):
"""Methods with the same name and without params should be equal."""
expected_method = mox.MockMethod("testMethod", [], False)
@@ -931,6 +990,7 @@ class MockObjectTest(unittest.TestCase):
self.assertEqual([x for x in dummy], ['X', 'Y'])
dummy._Verify()
+
def testMockContains_ExpectedContains_Success(self):
"""Test that __contains__ gets mocked in Dummy.
@@ -1421,6 +1481,26 @@ class MoxTest(unittest.TestCase):
self.assertEquals('foo', actual)
self.failIf(isinstance(test_obj.OtherValidCall, mox.MockAnything))
+ def testStubOutClass(self):
+ """Test a mocked class whose __init__ returns a Mock."""
+ self.mox.StubOutWithMock(mox_test_helper, 'TestClassFromAnotherModule')
+ self.assert_(isinstance(mox_test_helper.TestClassFromAnotherModule,
+ mox.MockObject))
+
+ mock_instance = self.mox.CreateMock(
+ mox_test_helper.TestClassFromAnotherModule)
+ mox_test_helper.TestClassFromAnotherModule().AndReturn(mock_instance)
+ mock_instance.Value().AndReturn('mock instance')
+
+ self.mox.ReplayAll()
+
+ a_mock = mox_test_helper.TestClassFromAnotherModule()
+ actual = a_mock.Value()
+
+ self.mox.VerifyAll()
+ self.mox.UnsetStubs()
+ self.assertEquals('mock instance', actual)
+
def testWarnsUserIfMockingMock(self):
"""Test that user is warned if they try to stub out a MockAnything."""
self.mox.StubOutWithMock(TestClass, 'MyStaticMethod')
@@ -1478,12 +1558,14 @@ class MoxTestBaseTest(unittest.TestCase):
def setUp(self):
self.mox = mox.Mox()
self.test_mox = mox.Mox()
+ self.test_stubs = mox.stubout.StubOutForTesting()
self.result = unittest.TestResult()
def tearDown(self):
- # In case one of our tests fail before UnsetStubs is called.
self.mox.UnsetStubs()
self.test_mox.UnsetStubs()
+ self.test_stubs.UnsetAll()
+ self.test_stubs.SmartUnsetAll()
def _setUpTestClass(self):
"""Replacement for setUp in the test class instance.
@@ -1493,6 +1575,7 @@ class MoxTestBaseTest(unittest.TestCase):
in the test class instance.
"""
self.test.mox = self.test_mox
+ self.test.stubs = self.test_stubs
def _CreateTest(self, test_name):
"""Create a test from our example mox class.
@@ -1506,14 +1589,17 @@ class MoxTestBaseTest(unittest.TestCase):
"""Run the checks to confirm test method completed successfully."""
self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
self.mox.StubOutWithMock(self.test_mox, 'VerifyAll')
+ self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+ self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
self.test_mox.UnsetStubs()
self.test_mox.VerifyAll()
+ self.test_stubs.UnsetAll()
+ self.test_stubs.SmartUnsetAll()
self.mox.ReplayAll()
self.test.run(result=self.result)
self.assertTrue(self.result.wasSuccessful())
- self.mox.UnsetStubs()
self.mox.VerifyAll()
- self.test_mox.UnsetStubs()
+ self.mox.UnsetStubs() # Needed to call the real VerifyAll() below.
self.test_mox.VerifyAll()
def testSuccess(self):
@@ -1521,47 +1607,78 @@ class MoxTestBaseTest(unittest.TestCase):
self._CreateTest('testSuccess')
self._VerifySuccess()
+ def testSuccessNoMocks(self):
+ """Let testSuccess() unset all the mocks, and verify they've been unset."""
+ self._CreateTest('testSuccess')
+ self.test.run(result=self.result)
+ self.assertTrue(self.result.wasSuccessful())
+ self.assertEqual(OS_LISTDIR, mox_test_helper.os.listdir)
+
+ def testStubs(self):
+ """Test that "self.stubs" is provided as is useful."""
+ self._CreateTest('testHasStubs')
+ self._VerifySuccess()
+
+ def testStubsNoMocks(self):
+ """Let testHasStubs() unset the stubs by itself."""
+ self._CreateTest('testHasStubs')
+ self.test.run(result=self.result)
+ self.assertTrue(self.result.wasSuccessful())
+ self.assertEqual(OS_LISTDIR, mox_test_helper.os.listdir)
+
def testExpectedNotCalled(self):
"""Stubbed out method is not called."""
self._CreateTest('testExpectedNotCalled')
self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
- # Dont stub out VerifyAll - that's what causes the test to fail
+ self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+ self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
+ # Don't stub out VerifyAll - that's what causes the test to fail
self.test_mox.UnsetStubs()
- self.test_mox.VerifyAll()
+ self.test_stubs.UnsetAll()
+ self.test_stubs.SmartUnsetAll()
self.mox.ReplayAll()
self.test.run(result=self.result)
self.failIf(self.result.wasSuccessful())
- self.mox.UnsetStubs()
self.mox.VerifyAll()
- self.test_mox.UnsetStubs()
+
+ def testExpectedNotCalledNoMocks(self):
+ """Let testExpectedNotCalled() unset all the mocks by itself."""
+ self._CreateTest('testExpectedNotCalled')
+ self.test.run(result=self.result)
+ self.failIf(self.result.wasSuccessful())
+ self.assertEqual(OS_LISTDIR, mox_test_helper.os.listdir)
def testUnexpectedCall(self):
"""Stubbed out method is called with unexpected arguments."""
self._CreateTest('testUnexpectedCall')
self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
+ self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+ self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
# Ensure no calls are made to VerifyAll()
self.mox.StubOutWithMock(self.test_mox, 'VerifyAll')
self.test_mox.UnsetStubs()
+ self.test_stubs.UnsetAll()
+ self.test_stubs.SmartUnsetAll()
self.mox.ReplayAll()
self.test.run(result=self.result)
self.failIf(self.result.wasSuccessful())
- self.mox.UnsetStubs()
self.mox.VerifyAll()
- self.test_mox.UnsetStubs()
def testFailure(self):
"""Failing assertion in test method."""
self._CreateTest('testFailure')
self.mox.StubOutWithMock(self.test_mox, 'UnsetStubs')
+ self.mox.StubOutWithMock(self.test_stubs, 'UnsetAll')
+ self.mox.StubOutWithMock(self.test_stubs, 'SmartUnsetAll')
# Ensure no calls are made to VerifyAll()
self.mox.StubOutWithMock(self.test_mox, 'VerifyAll')
self.test_mox.UnsetStubs()
+ self.test_stubs.UnsetAll()
+ self.test_stubs.SmartUnsetAll()
self.mox.ReplayAll()
self.test.run(result=self.result)
self.failIf(self.result.wasSuccessful())
- self.mox.UnsetStubs()
self.mox.VerifyAll()
- self.test_mox.UnsetStubs()
def testMixin(self):
"""Run test from mix-in test class, ensure it passes."""
@@ -1615,6 +1732,12 @@ class MyTestCase(unittest.TestCase):
def setUp(self):
super(MyTestCase, self).setUp()
self.critical_variable = 42
+ self.another_critical_variable = 42
+
+ def testMethodOverride(self):
+ """Should be properly overriden in a derived class."""
+ self.assertEquals(42, self.another_critical_variable)
+ self.another_critical_variable += 1
class MoxTestBaseMultipleInheritanceTest(mox.MoxTestBase, MyTestCase):
@@ -1622,12 +1745,20 @@ class MoxTestBaseMultipleInheritanceTest(mox.MoxTestBase, MyTestCase):
def setUp(self):
super(MoxTestBaseMultipleInheritanceTest, self).setUp()
+ self.another_critical_variable = 99
def testMultipleInheritance(self):
"""Should be able to access members created by all parent setUp()."""
self.assert_(isinstance(self.mox, mox.Mox))
self.assertEquals(42, self.critical_variable)
+ def testMethodOverride(self):
+ """Should run before MyTestCase.testMethodOverride."""
+ self.assertEquals(99, self.another_critical_variable)
+ self.another_critical_variable = 42
+ super(MoxTestBaseMultipleInheritanceTest, self).testMethodOverride()
+ self.assertEquals(43, self.another_critical_variable)
+
class TestClass:
"""This class is used only for testing the mock framework"""
diff --git a/mox_test_helper.py b/mox_test_helper.py
index 164c0ec..b4bfdec 100755
--- a/mox_test_helper.py
+++ b/mox_test_helper.py
@@ -74,3 +74,22 @@ class ExampleMoxTest(mox.MoxTestBase, ExampleMoxTestMixin):
os.stat(self.DIR_PATH)
self.mox.ReplayAll()
os.stat(self.DIR_PATH)
+
+ def testHasStubs(self):
+ listdir_list = []
+
+ def MockListdir(directory):
+ listdir_list.append(directory)
+
+ self.stubs.Set(os, 'listdir', MockListdir)
+ os.listdir(self.DIR_PATH)
+ self.assertEqual([self.DIR_PATH], listdir_list)
+
+
+class TestClassFromAnotherModule(object):
+
+ def __init__():
+ return None
+
+ def Value():
+ return "Not mock"