170 lines
6.5 KiB
Python
170 lines
6.5 KiB
Python
# Copyright (c) 2010 Twisted Matrix Laboratories.
|
|
# See LICENSE for details.
|
|
|
|
"""Tests for testtools.monkey."""
|
|
|
|
from testtools import TestCase
|
|
from testtools.matchers import MatchesException, Raises
|
|
from testtools.monkey import MonkeyPatcher, patch
|
|
|
|
|
|
class TestObj:
|
|
|
|
def __init__(self):
|
|
self.foo = 'foo value'
|
|
self.bar = 'bar value'
|
|
self.baz = 'baz value'
|
|
|
|
|
|
class MonkeyPatcherTest(TestCase):
|
|
"""
|
|
Tests for 'MonkeyPatcher' monkey-patching class.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.test_object = TestObj()
|
|
self.original_object = TestObj()
|
|
self.monkey_patcher = MonkeyPatcher()
|
|
|
|
def test_empty(self):
|
|
# A monkey patcher without patches doesn't change a thing.
|
|
self.monkey_patcher.patch()
|
|
|
|
# We can't assert that all state is unchanged, but at least we can
|
|
# check our test object.
|
|
self.assertEqual(self.original_object.foo, self.test_object.foo)
|
|
self.assertEqual(self.original_object.bar, self.test_object.bar)
|
|
self.assertEqual(self.original_object.baz, self.test_object.baz)
|
|
|
|
def test_construct_with_patches(self):
|
|
# Constructing a 'MonkeyPatcher' with patches adds all of the given
|
|
# patches to the patch list.
|
|
patcher = MonkeyPatcher((self.test_object, 'foo', 'haha'),
|
|
(self.test_object, 'bar', 'hehe'))
|
|
patcher.patch()
|
|
self.assertEqual('haha', self.test_object.foo)
|
|
self.assertEqual('hehe', self.test_object.bar)
|
|
self.assertEqual(self.original_object.baz, self.test_object.baz)
|
|
|
|
def test_patch_existing(self):
|
|
# Patching an attribute that exists sets it to the value defined in the
|
|
# patch.
|
|
self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha')
|
|
self.monkey_patcher.patch()
|
|
self.assertEqual(self.test_object.foo, 'haha')
|
|
|
|
def test_patch_non_existing(self):
|
|
# Patching a non-existing attribute sets it to the value defined in
|
|
# the patch.
|
|
self.monkey_patcher.add_patch(self.test_object, 'doesntexist', 'value')
|
|
self.monkey_patcher.patch()
|
|
self.assertEqual(self.test_object.doesntexist, 'value')
|
|
|
|
def test_restore_non_existing(self):
|
|
# Restoring a value that didn't exist before the patch deletes the
|
|
# value.
|
|
self.monkey_patcher.add_patch(self.test_object, 'doesntexist', 'value')
|
|
self.monkey_patcher.patch()
|
|
self.monkey_patcher.restore()
|
|
marker = object()
|
|
self.assertIs(marker, getattr(self.test_object, 'doesntexist', marker))
|
|
|
|
def test_patch_already_patched(self):
|
|
# Adding a patch for an object and attribute that already have a patch
|
|
# overrides the existing patch.
|
|
self.monkey_patcher.add_patch(self.test_object, 'foo', 'blah')
|
|
self.monkey_patcher.add_patch(self.test_object, 'foo', 'BLAH')
|
|
self.monkey_patcher.patch()
|
|
self.assertEqual(self.test_object.foo, 'BLAH')
|
|
self.monkey_patcher.restore()
|
|
self.assertEqual(self.test_object.foo, self.original_object.foo)
|
|
|
|
def test_restore_twice_is_a_no_op(self):
|
|
# Restoring an already-restored monkey patch is a no-op.
|
|
self.monkey_patcher.add_patch(self.test_object, 'foo', 'blah')
|
|
self.monkey_patcher.patch()
|
|
self.monkey_patcher.restore()
|
|
self.assertEqual(self.test_object.foo, self.original_object.foo)
|
|
self.monkey_patcher.restore()
|
|
self.assertEqual(self.test_object.foo, self.original_object.foo)
|
|
|
|
def test_run_with_patches_decoration(self):
|
|
# run_with_patches runs the given callable, passing in all arguments
|
|
# and keyword arguments, and returns the return value of the callable.
|
|
log = []
|
|
|
|
def f(a, b, c=None):
|
|
log.append((a, b, c))
|
|
return 'foo'
|
|
|
|
result = self.monkey_patcher.run_with_patches(f, 1, 2, c=10)
|
|
self.assertEqual('foo', result)
|
|
self.assertEqual([(1, 2, 10)], log)
|
|
|
|
def test_repeated_run_with_patches(self):
|
|
# We can call the same function with run_with_patches more than
|
|
# once. All patches apply for each call.
|
|
def f():
|
|
return (self.test_object.foo, self.test_object.bar,
|
|
self.test_object.baz)
|
|
|
|
self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha')
|
|
result = self.monkey_patcher.run_with_patches(f)
|
|
self.assertEqual(
|
|
('haha', self.original_object.bar, self.original_object.baz),
|
|
result
|
|
)
|
|
result = self.monkey_patcher.run_with_patches(f)
|
|
self.assertEqual(
|
|
('haha', self.original_object.bar, self.original_object.baz),
|
|
result
|
|
)
|
|
|
|
def test_run_with_patches_restores(self):
|
|
# run_with_patches restores the original values after the function has
|
|
# executed.
|
|
self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha')
|
|
self.assertEqual(self.original_object.foo, self.test_object.foo)
|
|
self.monkey_patcher.run_with_patches(lambda: None)
|
|
self.assertEqual(self.original_object.foo, self.test_object.foo)
|
|
|
|
def test_run_with_patches_restores_on_exception(self):
|
|
# run_with_patches restores the original values even when the function
|
|
# raises an exception.
|
|
def _():
|
|
self.assertEqual(self.test_object.foo, 'haha')
|
|
self.assertEqual(self.test_object.bar, 'blahblah')
|
|
raise RuntimeError("Something went wrong!")
|
|
|
|
self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha')
|
|
self.monkey_patcher.add_patch(self.test_object, 'bar', 'blahblah')
|
|
|
|
self.assertThat(lambda:self.monkey_patcher.run_with_patches(_),
|
|
Raises(MatchesException(RuntimeError("Something went wrong!"))))
|
|
self.assertEqual(self.test_object.foo, self.original_object.foo)
|
|
self.assertEqual(self.test_object.bar, self.original_object.bar)
|
|
|
|
|
|
class TestPatchHelper(TestCase):
|
|
|
|
def test_patch_patches(self):
|
|
# patch(obj, name, value) sets obj.name to value.
|
|
test_object = TestObj()
|
|
patch(test_object, 'foo', 42)
|
|
self.assertEqual(42, test_object.foo)
|
|
|
|
def test_patch_returns_cleanup(self):
|
|
# patch(obj, name, value) returns a nullary callable that restores obj
|
|
# to its original state when run.
|
|
test_object = TestObj()
|
|
original = test_object.foo
|
|
cleanup = patch(test_object, 'foo', 42)
|
|
cleanup()
|
|
self.assertEqual(original, test_object.foo)
|
|
|
|
|
|
def test_suite():
|
|
from unittest import TestLoader
|
|
return TestLoader().loadTestsFromName(__name__)
|