def with_type_validation_False(context): context.memoize("type_validation", lambda self: False) @context.example def it_passes_with_invalid_types(self): self.target(message=1234)
def mock_constructor(context): context.memoize("target_module", lambda self: sys.modules[__name__]) context.memoize_before("target_class_name", lambda self: target_class_name) @context.function def get_target_class(self): return getattr(self.target_module, self.target_class_name) @context.function @contextlib.contextmanager def assertRaisesWithMessageInException(self, exception, msg): with self.assertRaises(exception) as cm: yield ex_msg = str(cm.exception) self.assertIn( msg, ex_msg, "Expected exception {}.{} message " "to be\n{}\nbut got\n{}.".format(exception.__module__, exception.__name__, repr(msg), repr(ex_msg)), ) @context.before def assert_unpatched(self): self.assertTrue(original_target_class is self.get_target_class(), "Unpatching didn't work.") args = (1, 2) kwargs = {"3": 4, "5": 6} t = Target(*args, **kwargs) self.assertEqual(type(t), original_target_class) self.assertEqual(t.args, args) self.assertEqual(t.kwargs, kwargs) @context.shared_context def class_attributes(context): @context.example def attributes_are_not_affected(self): self.assertEqual(self.class_attribute_target.CLASS_ATTR, "CLASS_ATTR") @context.example def static_methods_are_not_affected(self): self.assertEqual(self.class_attribute_target.static_method(), "static_method") @context.sub_context def class_methods(context): @context.example def are_not_affected(self): self.assertEqual( self.class_attribute_target.regular_class_method(), "regular_class_method", ) @context.example("super(Target, cls) works") def p2_super_works(self): self.assertEqual( self.class_attribute_target.p2_super_class_method(), "p2_super_class_method", ) @context.example("super() works") def p3_super_works(self): self.assertEqual( self.class_attribute_target.p3_super_class_method(), "p3_super_class_method", ) @context.sub_context def patching_mechanism(context): @context.example def can_not_mock_constructor_with_existing_instances(self): original_target = original_target_class() with self.assertRaisesWithMessageInException( RuntimeError, "mock_constructor() can not be used after instances of Target were created: {}" .format([original_target]), ): self.mock_constructor( self.target_module, self.target_class_name).to_call_original() @context.example def works_with_composition(self): self.mock_constructor( self.target_module, self.target_class_name).for_call( "1").with_wrapper(lambda original_callable, *args, ** kwargs: original_callable("one")) self.mock_constructor( self.target_module, self.target_class_name).for_call( "2").with_wrapper(lambda original_callable, *args, ** kwargs: original_callable("two")) target_one = self.get_target_class()("1") self.assertEqual(target_one.args, ("one", )) target_two = self.get_target_class()("2") self.assertEqual(target_two.args, ("two", )) @context.sub_context def original_class_attribute_access(context): @context.before def mock_constructor(self): self.mock_constructor( self.target_module, self.target_class_name).to_call_original() @context.example def can_not_create_new_instances(self): with self.assertRaisesWithMessageInException( BaseException, "Attribute getting after the class has been used with mock_constructor() is not supported!", ): original_target_class() @context.example def can_access_class_attributes(self): self.assertEqual(original_target_class.CLASS_ATTR, "CLASS_ATTR") @context.example def can_call_class_methods(self): for name in [ "regular_class_method", "p2_super_class_method", "p3_super_class_method", ]: self.assertEqual( getattr(original_target_class, name)(), name) @context.example def can_call_static_methods(self): self.assertEqual(original_target_class.static_method(), "static_method") @context.sub_context def arguments(context): @context.example def refuses_to_mock_if_instances_exist(self): target_instance = self.get_target_class()() # noqa F841 with self.assertRaisesWithMessageInException( RuntimeError, "mock_constructor() can not be used after instances of Target were created: ", ): self.mock_constructor(self.target_module, self.target_class_name) @context.sub_context def module(context): context.memoize("args", lambda self: ("6", "7")) context.memoize("kwargs", lambda self: {"8": "eight", "9": "nine"}) @context.after def assert_working(self): mocked_instance = self.get_target_class()(*self.args, **self.kwargs) self.assertEqual(mocked_instance, "mocked") @context.example def accepts_string(self): self.mock_constructor( self.target_module.__name__, self.target_class_name).for_call( *self.args, **self.kwargs).to_return_value("mocked") @context.example def accepts_reference(self): self.mock_constructor( self.target_module, self.target_class_name).for_call( *self.args, **self.kwargs).to_return_value("mocked") @context.sub_context("class") def klass(context): @context.example def rejects_non_string_class_name(self): with self.assertRaisesWithMessageInException( ValueError, "Second argument must be a string with the name of the class.", ): self.mock_constructor(self.target_module, original_target_class) @context.example def rejects_non_class_targets(self): with self.assertRaisesWithMessageInException( ValueError, "Target must be a class."): self.mock_constructor(self.target_module, "function_at_module") @context.sub_context def class_attributes_at_the_class(context): @context.memoize def class_attribute_target(self): return self.get_target_class() context.merge_context("class attributes") @context.sub_context("mock_callable() integration") def mock_callable_integration(context): @context.example def it_uses_mock_callable_interface(self): self.assertIsInstance( self.mock_constructor(self.target_module, self.target_class_name), _MockCallableDSL, ) @context.example def registers_call_count_and_args_correctly(self): self.mock_constructor( self.target_module, self.target_class_name).for_call( "Hello", "World").to_return_value(None).and_assert_called_exactly(2) target_class = self.get_target_class() t1 = target_class("Hello", "World") t2 = target_class("Hello", "World") self.assertIsNone(t1) self.assertIsNone(t2) @context.example def mock_constructor_can_not_assert_if_already_received_call(self): mock = (self.mock_constructor(self.target_module, self.target_class_name).for_call( "Hello", "World").to_return_value(None)) target_class = self.get_target_class() target_class("Hello", "World") with self.assertRaisesRegex( ValueError, "^No extra configuration is allowed after mock_constructor.+self.mock_constructor", ): mock.and_assert_called_once() @context.sub_context def behavior(context): @context.example(".to_call_original() works") def to_call_original_works(self): default_args = ("default", ) specific_args = ("specific", ) self.mock_constructor( self.target_module, self.target_class_name).to_call_original() self.mock_constructor( self.target_module, self.target_class_name).for_call( *specific_args).to_return_value("mocked_target") default_target = self.get_target_class()(*default_args) self.assertEqual(default_target.args, default_args) specific_target = self.get_target_class()(*specific_args) self.assertEqual(specific_target, "mocked_target") @context.example(".with_implementation() works") def with_implementation_works(self): args = ("1", "2") kwargs = {"one": "2", "two": "2"} def implementation(*received_args, **received_kwargs): self.assertEqual(received_args, args) self.assertEqual(received_kwargs, kwargs) return "mock" self.mock_constructor( self.target_module, self.target_class_name).with_implementation(implementation) self.assertEqual(self.get_target_class()(*args, **kwargs), "mock") @context.sub_context(".with_wrapper()") def with_wrapper(context): context.memoize("args", lambda self: ("1", "2")) context.memoize("wrapped_args", lambda self: ("3", "4")) context.memoize("kwargs", lambda self: { "one": "1", "two": "2" }) context.memoize("wrapped_kwargs", lambda self: { "three": "3", "four": "4" }) @context.memoize def target(self): return self.get_target_class()(*self.args, **self.kwargs) @context.before def setup_wrapper(self): def wrapper(original_callable, *args, **kwargs): return original_callable(*self.wrapped_args, **self.wrapped_kwargs) self.mock_constructor( self.target_module, self.target_class_name).with_wrapper(wrapper) @context.example def wrapped_instance_is_instance_of_original_class(self): self.assertIsInstance(self.target, original_target_class) @context.example def constructor_is_wrapped(self): self.assertSequenceEqual(self.target.args, self.wrapped_args) self.assertSequenceEqual(self.target.kwargs, self.wrapped_kwargs) @context.example def factory_works(self): def factory(original_callable, message): return "got: {}".format(message) self.mock_constructor(self.target_module, self.target_class_name).for_call( "factory").with_wrapper(factory) target = self.get_target_class()("factory") self.assertEqual(target, "got: factory") @context.sub_context def class_attributes_at_the_instance(context): context.memoize("class_attribute_target", lambda self: self.target) context.merge_context("class attributes") @context.sub_context("Target.__init__()") def target_init(context): @context.example("super(Target, self)") def p2_super_works(self): target = self.get_target_class()(p2_super=True) self.assertTrue(target.p2_super) @context.example("super() works") def p3_super_works(self): target = self.get_target_class()(p3_super=True) self.assertTrue(target.p3_super) @context.example def can_be_called_again(self): new_args = ("new", "args") new_kwargs = {"new": "kwargs"} self.target.__init__(*new_args, **new_kwargs) self.assertEqual(self.target.args, new_args) self.assertEqual(self.target.kwargs, new_kwargs) @context.sub_context def instance_methods(context): @context.example def it_works(self): self.assertEqual( self.target.regular_instance_method(), "regular_instance_method", ) @context.sub_context def when_it_overloads_parent_method(context): @context.example("super(Target, self) works") def p2_super_works(self): self.assertEqual( self.target.p2_super_instance_method(), "p2_super_instance_method", ) @context.example("super() works") def p3_super_works(self): self.assertEqual( self.target.p3_super_instance_method(), "p3_super_instance_method", ) @context.sub_context def StrictMock_integration(context): @context.shared_context def StrictMock_tests(context): @context.before def setup(self): self.mock_constructor( self.target_module, self.target_class_name).for_call().to_return_value( self.target_mock) self.target = self.get_target_class()() @context.example def patching_works(self): self.assertIs(self.target, self.target_mock) @context.example("mock_callable() works") def mock_callable_works(self): self.mock_callable( self.target_mock, "regular_instance_method").for_call().to_return_value( "mocked") self.assertEqual(self.target.regular_instance_method(), "mocked") @context.example def dynamic_attributes_work(self): self.target_mock.dynamic_attr = "mocked_attr" self.assertEqual(self.target.dynamic_attr, "mocked_attr") @context.function def get_target_mock(self): return StrictMock(template=self.get_target_class()) @context.sub_context def with_target_mock_memoized_before(context): @context.memoize_before def target_mock(self): return self.get_target_mock() context.merge_context("StrictMock tests") @context.sub_context def with_target_mock_memoized(context): @context.memoize def target_mock(self): return self.get_target_mock() context.merge_context("StrictMock tests") @context.example def private_patching_raises_valueerror(self): with self.assertRaises(ValueError): self.mock_constructor(self.target_module, _PrivateClass.__name__) @context.example def private_patching_allow_private(self): self.mock_constructor( self.target_module, _PrivateClass.__name__, allow_private=True).for_call().to_return_value("mocked_private") _PrivateClass() @context.sub_context def type_validation(context): context.memoize("value", lambda self: "Target mock") context.memoize("type_validation", lambda self: True) @context.before def before(self): self.mock_constructor( self.target_module, self.target_class_name, type_validation=self.type_validation, ).to_return_value(self.value) self.target = getattr(self.target_module, self.target_class_name) @context.example def it_passes_with_valid_types(self): self.assertEqual(self.target(message="hello"), self.value) @context.example def it_fails_with_invalid_types(self): with self.assertRaises(TypeCheckError): self.target(message=1234) @context.sub_context("with type_validation=False") def with_type_validation_False(context): context.memoize("type_validation", lambda self: False) @context.example def it_passes_with_invalid_types(self): self.target(message=1234)
def with_wrapper(context): context.memoize("args", lambda self: ("1", "2")) context.memoize("wrapped_args", lambda self: ("3", "4")) context.memoize("kwargs", lambda self: { "one": "1", "two": "2" }) context.memoize("wrapped_kwargs", lambda self: { "three": "3", "four": "4" }) @context.memoize def target(self): return self.get_target_class()(*self.args, **self.kwargs) @context.before def setup_wrapper(self): def wrapper(original_callable, *args, **kwargs): return original_callable(*self.wrapped_args, **self.wrapped_kwargs) self.mock_constructor( self.target_module, self.target_class_name).with_wrapper(wrapper) @context.example def wrapped_instance_is_instance_of_original_class(self): self.assertIsInstance(self.target, original_target_class) @context.example def constructor_is_wrapped(self): self.assertSequenceEqual(self.target.args, self.wrapped_args) self.assertSequenceEqual(self.target.kwargs, self.wrapped_kwargs) @context.example def factory_works(self): def factory(original_callable, message): return "got: {}".format(message) self.mock_constructor(self.target_module, self.target_class_name).for_call( "factory").with_wrapper(factory) target = self.get_target_class()("factory") self.assertEqual(target, "got: factory") @context.sub_context def class_attributes_at_the_instance(context): context.memoize("class_attribute_target", lambda self: self.target) context.merge_context("class attributes") @context.sub_context("Target.__init__()") def target_init(context): @context.example("super(Target, self)") def p2_super_works(self): target = self.get_target_class()(p2_super=True) self.assertTrue(target.p2_super) @context.example("super() works") def p3_super_works(self): target = self.get_target_class()(p3_super=True) self.assertTrue(target.p3_super) @context.example def can_be_called_again(self): new_args = ("new", "args") new_kwargs = {"new": "kwargs"} self.target.__init__(*new_args, **new_kwargs) self.assertEqual(self.target.args, new_args) self.assertEqual(self.target.kwargs, new_kwargs) @context.sub_context def instance_methods(context): @context.example def it_works(self): self.assertEqual( self.target.regular_instance_method(), "regular_instance_method", ) @context.sub_context def when_it_overloads_parent_method(context): @context.example("super(Target, self) works") def p2_super_works(self): self.assertEqual( self.target.p2_super_instance_method(), "p2_super_instance_method", ) @context.example("super() works") def p3_super_works(self): self.assertEqual( self.target.p3_super_instance_method(), "p3_super_instance_method", )
def class_attributes_at_the_instance(context): context.memoize("class_attribute_target", lambda self: self.target) context.merge_context("class attributes")
def composition(context): """ This context takes care of composition of multiple call/behavior/assertion combination, to ensure they play along well. """ context.memoize( "other_args", lambda self: ["other_arg" for arg in self.call_args]) context.memoize( "other_kwargs", lambda self: {k: "other_value" for k, v in self.call_kwargs.items()}, ) @context.example def newest_mock_has_precedence_over_older_mocks(self): """ Mocks are designed to be composable, allowing us to declare multiple behaviors for different calls. Those definitions stack up, and when a call is made to the mock, they are searched from newest to oldest, to find one that is able to be caled. """ # First, mock all calls mock_callable( self.target_arg, self.callable_arg).to_return_value("any args") # Then we add some specific call behavior mock_callable(self.target_arg, self.callable_arg).for_call( *self.specific_call_args, ** self.specific_call_kwargs).to_return_value("specific") # The first behavior should still be there self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), "any args", ) # as well as the specific case self.assertEqual( self.callable_target(*self.specific_call_args, **self.specific_call_kwargs), "specific", ) # but if we add another "catch all" case mock_callable( self.target_arg, self.callable_arg).to_return_value("new any args") # it should take over any previous mock self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), "new any args", ) self.assertEqual( self.callable_target(*self.specific_call_args, **self.specific_call_kwargs), "new any args", ) @context.sub_context def multiple_assertions(context): @context.before def setup_mocks(self): mock_callable(self.target_arg, self.callable_arg).to_return_value( "any args").and_assert_called_once() mock_callable( self.target_arg, self.callable_arg).for_call( *self.specific_call_args, **self.specific_call_kwargs).to_return_value( "specific").and_assert_called_twice() @context.example def that_passes(self): self.callable_target(*self.other_args, **self.other_kwargs) self.callable_target(*self.specific_call_args, **self.specific_call_kwargs) self.callable_target(*self.specific_call_args, **self.specific_call_kwargs) @context.example def that_fails(self): # "Pass" this test when callable accepts no arguments if (self.specific_call_args == self.call_args and self.specific_call_kwargs == self.call_kwargs): raise RuntimeError("FIXME") return self.callable_target(*self.other_args, **self.other_kwargs) self.callable_target(*self.specific_call_args, **self.specific_call_kwargs) with self.assertRaises(AssertionError): self.assert_all()
def with_magic_method_defined_on_parent_class(context): context.memoize("target", lambda self: Target()) context.merge_context("magic method tests")
def without_template(context): context.memoize("strict_mock", lambda _: StrictMock()) @context.memoize def strict_mock_rgx(self): return ("<StrictMock 0x{:02X} ".format(id(self.strict_mock)) + re.escape(self.caller_filename) + ":\d+>") context.memoize("value", lambda _: 3241234123) context.memoize("test_method_name", lambda _: "some_method") context.memoize("mock_function", lambda _: lambda: None) context.merge_context("can access attributes") @context.example def raises_when_an_undefined_attribute_is_accessed(self): name = "undefined_attribute" with self.assertRaisesWithRegexMessage( AttributeError, f"'{name}' was not set for {self.strict_mock}."): getattr(self.strict_mock, name) @context.example def allows_mocking_any_attribute(self): self.strict_mock.any_attribute = self.value self.assertEqual(self.strict_mock.any_attribute, self.value) @context.example def allows_deleting_a_mocked_attribute(self): name = "attr_name" setattr(self.strict_mock, name, self.value) self.assertTrue(hasattr(self.strict_mock, name)) delattr(self.strict_mock, name) with self.assertRaisesWithRegexMessage( AttributeError, f"'{name}' was not set for {self.strict_mock}."): getattr(self.strict_mock, name) @context.example def allows_mocking_any_method(self): def value_plus(b): return self.value + b self.strict_mock.any_method = value_plus plus = 2341 self.assertEqual(self.strict_mock.any_method(plus), self.value + plus) @context.example def allows_mocking_context_manager_methods(self): enter_mock = "something" self.strict_mock.__enter__ = lambda: enter_mock self.strict_mock.__exit__ = lambda exc_type, exc_value, traceback: None with self.strict_mock as target: self.assertEqual(target, enter_mock) @context.example def attribute_type_is_maintained(self): callable_attr = CallableObject() self.strict_mock.callable_attr = callable_attr attr = {1: 2} self.strict_mock.attr = attr self.assertEqual(type(self.strict_mock.callable_attr), type(callable_attr)) self.assertEqual(type(self.strict_mock.attr), type(attr))
def mock_callable_context(context): ## ## Common mock_callable setup ## context.memoize("assertions", lambda _: []) context.memoize("call_args", lambda _: ("first", "second")) context.memoize("call_kwargs", lambda _: { "kwarg1": "first", "kwarg2": "second" }) @context.memoize def specific_call_args(self): return tuple("specific {}".format(arg) for arg in self.call_args) @context.memoize def specific_call_kwargs(self): return { k: "specific {}".format(v) for k, v in self.call_kwargs.items() } @context.before def register_assertions(self): def register_assertion(assertion): self.assertions.append(assertion) testslide.mock_callable.register_assertion = register_assertion @context.after def cleanup_patches(self): # Unpatch before assertions, to make sure it is done if assertion fails. testslide.mock_callable.unpatch_all_callable_mocks() for assertion in self.assertions: assertion() @context.function @contextlib.contextmanager def assertRaisesWithMessage(self, exception, msg): with self.assertRaises(exception) as cm: yield ex_msg = str(cm.exception) self.assertEqual( ex_msg, msg, "Expected exception {}.{} message " "to be\n{}\nbut got\n{}.".format(exception.__module__, exception.__name__, repr(msg), repr(ex_msg)), ) @context.shared_context def examples_for_target( context, callable_accepts_no_args=False, has_original_callable=True, can_yield=True, validate_signature=True, ): @context.function def assert_all(self): try: for assertion in self.assertions: assertion() finally: del self.assertions[:] @context.function def no_behavior_msg(self): if self.call_kwargs: kwargs_msg = (" {\n" + "".join( " {}={},\n".format(k, self.call_kwargs[k]) for k in sorted(self.call_kwargs.keys())) + " }\n") else: kwargs_msg = " {}\n" return str("{}, {}:\n".format(repr(self.target_arg), repr(self.callable_arg)) + " Received call:\n" + " {}\n".format(self.call_args) + kwargs_msg + " But no behavior was defined for it.") @context.shared_context def mock_call_arguments(context): @context.example def works_for_matching_signature(self): self.callable_target(*self.call_args, **self.call_kwargs), if validate_signature and sys.version_info[0] != 2: @context.example def raises_TypeError_for_mismatching_signature(self): args = ("some", "invalid", "args", "list") kwargs = {"invalid_kwarg": "invalid_value"} with self.assertRaises(TypeError): self.callable_target(*args, **kwargs) @context.sub_context(".for_call(*args, **kwargs)") def for_call_args_kwargs(context): if not callable_accepts_no_args: @context.sub_context def with_matching_signature(context): @context.before def before(self): self.mock_callable_dsl.for_call( *self.specific_call_args, **self.specific_call_kwargs) @context.example def it_accepts_known_arguments(self): self.callable_target(*self.specific_call_args, **self.specific_call_kwargs) if validate_signature: @context.example def it_rejects_unknown_arguments(self): with self.assertRaisesWithMessage( UnexpectedCallArguments, self.no_behavior_msg() + "\n These are the registered calls:\n" + " {}\n".format( self.specific_call_args) + " {\n" + "".join(" {}={},\n".format( k, self.specific_call_kwargs[k]) for k in sorted( self.specific_call_kwargs. keys())) + " }\n", ): self.callable_target( *self.call_args, **self.call_kwargs) if validate_signature: @context.sub_context def with_mismatching_signature(context): @context.xexample def it_fails_to_mock(self): with self.assertRaisesWithMessage( ValueError, "Can not mock target for arguments that mismatch the " "original callable signature.", ): self.mock_callable_dsl.for_call( "some", "invalid", "args", and_some="invalid", kwargs="values", ) @context.shared_context def assertions(context): @context.shared_context def assert_failure(context): @context.after def after(self): with self.assertRaises(AssertionError): self.assert_all() @context.shared_context def not_called(context): @context.example def not_called(self): pass @context.shared_context def called_less_times(context): @context.example def called_less_times(self): for _ in range(self.times - 1): self.callable_target(*self.call_args, **self.call_kwargs) @context.shared_context def called_more_times(context): @context.example def called_more_times(self): for _ in range(self.times + 1): self.callable_target(*self.call_args, **self.call_kwargs) @context.shared_context def called_more_times_fail(context): @context.example def called_more_times(self): for _ in range(self.times): self.callable_target(*self.call_args, **self.call_kwargs) with self.assertRaisesWithMessage( UnexpectedCallReceived, ("Unexpected call received.\n" "{}, {}:\n" " expected to receive at most {} calls with any arguments " " but received an extra call.").format( repr(self.target_arg), repr(self.callable_arg), self.times), ): self.callable_target(*self.call_args, **self.call_kwargs) @context.shared_context def called_exactly_times(context): @context.example def called_exactly_times(self): for _ in range(self.times): self.callable_target(*self.call_args, **self.call_kwargs) @context.sub_context(".and_assert_called_exactly(times)") def and_assert_called_exactly(context): @context.sub_context def with_valid_input(context): @context.before def setup_assertion(self): self.mock_callable_dsl.and_assert_called_exactly( self.times) @context.sub_context def fails_when(context): context.merge_context("assert failure") context.merge_context("not called") context.merge_context("called less times") context.merge_context("called more times fail") @context.sub_context def passes_when(context): context.merge_context("called exactly times") @context.sub_context(".and_assert_called_at_least(times)") def and_assert_called_at_least(context): @context.sub_context def with_invalid_input(context): @context.example("fails to mock when times < 1") def fails_to_mock_when_times_1(self): with self.assertRaisesWithMessage( ValueError, "times must be >= 1"): self.mock_callable_dsl.and_assert_called_at_least( 0) @context.sub_context def with_valid_input(context): @context.before def setup_assertion(self): self.mock_callable_dsl.and_assert_called_at_least( self.times) @context.sub_context def fails_when(context): context.merge_context("assert failure") context.merge_context("not called") context.merge_context("called less times") @context.sub_context def passes_when(context): context.merge_context("called exactly times") context.merge_context("called more times") @context.sub_context(".and_assert_called_at_most(times)") def and_assert_called_at_most(context): @context.sub_context def with_invalid_input(context): @context.example("fails to mock when times < 1") def fails_to_mock_when_times_1(self): with self.assertRaisesWithMessage( ValueError, "times must be >= 1"): self.mock_callable_dsl.and_assert_called_at_most(0) @context.sub_context def with_valid_input(context): @context.before def setup_assertion(self): self.mock_callable_dsl.and_assert_called_at_most( self.times) @context.sub_context def fails_when(context): context.merge_context("assert failure") context.merge_context("not called") context.merge_context("called more times fail") @context.sub_context def passes_when(context): context.merge_context("called less times") context.merge_context("called exactly times") @context.sub_context(".and_assert_called()") def and_assert_called(context): @context.before def setup_assertion(self): self.mock_callable_dsl.and_assert_called() @context.sub_context def fails_when(context): context.merge_context("assert failure") context.merge_context("not called") @context.sub_context def passes_when(context): @context.example def called_once(self): self.callable_target(*self.call_args, **self.call_kwargs) @context.example def called_several_times(self): for _ in range(self.times + 1): self.callable_target(*self.call_args, **self.call_kwargs) @context.sub_context(".and_assert_not_called()") def and_assert_not_called(context): @context.example def can_use_with_previously_existing_behavior(self): self.mock_callable_dsl.and_assert_not_called() @context.sub_context def default_behavior(context): @context.example def mock_call_fails_with_undefined_behavior(self): with self.assertRaisesWithMessage(UndefinedBehaviorForCall, self.no_behavior_msg()): self.callable_target(*self.call_args, **self.call_kwargs) @context.sub_context(".and_assert_not_called()") def and_assert_not_called(context): @context.before def setup_assertion(self): self.mock_callable_dsl.and_assert_not_called() @context.sub_context def passes_when(context): @context.example def not_called(self): pass @context.sub_context def fails_when(context): @context.after def after(self): with self.assertRaises(AssertionError): self.assert_all() @context.example def called(self): with self.assertRaisesWithMessage( UnexpectedCallReceived, "{}, {}: Excepted not to be called!".format( repr(self.real_target), repr(self.callable_arg)), ): self.callable_target(*self.call_args, **self.call_kwargs) @context.sub_context(".to_return(value)") def to_return_value(context): context.memoize("value", lambda _: "mocked value") context.memoize("times", lambda _: 3) @context.before def setup_mock(self): self.mock_callable_dsl.to_return_value(self.value) if has_original_callable: context.nest_context("mock call arguments") context.nest_context("assertions") @context.example def mock_call_returns_given_value(self): self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), self.value, ) other_args = ["other_arg" for arg in self.call_args] other_kwargs = {k: "other_value" for k in self.call_kwargs} self.assertEqual( self.callable_target(*other_args, **other_kwargs), self.value) @context.sub_context(".to_return_values(values_list)") def to_return_values_values_list(context): context.memoize("values_list", lambda _: ["first", "second", "third"]) context.memoize("times", lambda self: len(self.values_list) - 1) @context.before def setup_mock(self): self.mock_callable_dsl.to_return_values(self.values_list) if has_original_callable: context.nest_context("mock call arguments") context.nest_context("assertions") @context.example def return_values_from_list_in_order(self): for value in self.values_list: self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), value) @context.sub_context def when_list_is_exhausted(context): @context.before def before(self): for _ in self.values_list: self.callable_target(*self.call_args, **self.call_kwargs) @context.example def it_raises(self): with self.assertRaisesWithMessage( UndefinedBehaviorForCall, "No more values to return!"): self.callable_target(*self.call_args, **self.call_kwargs) if can_yield: @context.sub_context(".to_yield_values(values_list)") def to_yield_values_values_list(context): context.memoize("values_list", lambda _: ["first", "second", "third"]) context.memoize("times", lambda self: len(self.values_list) - 1) @context.before def setup_mock(self): self.mock_callable_dsl.to_yield_values(self.values_list) if has_original_callable: context.nest_context("mock call arguments") context.nest_context("assertions") @context.memoize def iterable(self): return iter( self.callable_target(*self.call_args, **self.call_kwargs)) @context.example def yield_values_from_list_in_order(self): for value in self.values_list: self.assertEqual(next(self.iterable), value) @context.sub_context def when_list_is_empty(context): @context.before def before(self): for _ in self.values_list: next(self.iterable) @context.example def it_raises_StopIteration(self): with self.assertRaises(StopIteration): next(self.iterable) @context.sub_context(".to_raise(exception)") def to_raise_exception(context): context.memoize("exception_class", lambda _: RuntimeError) context.memoize("times", lambda _: 3) @context.shared_context def integration(context): @context.before def catch_callable_target_exceptions(self): original_callable_target = self.callable_target def _callable_target(*args, **kwargs): with self.assertRaises(self.exception_class): return original_callable_target(*args, **kwargs) self.callable_target = _callable_target if has_original_callable: context.nest_context("mock call arguments") context.nest_context("assertions") @context.sub_context def when_given_an_exception_class(context): @context.before def setup_mock(self): self.mock_callable_dsl.to_raise(self.exception_class) @context.example def it_raises_an_instance_of_the_class(self): with self.assertRaises(self.exception_class): self.callable_target(*self.call_args, **self.call_kwargs) context.nest_context("integration") @context.sub_context def when_given_an_exception_instance(context): context.memoize("exception_message", lambda _: "test exception") context.memoize( "exception", lambda self: self.exception_class(self.exception_message), ) @context.before def setup_mock(self): self.mock_callable_dsl.to_raise(self.exception) @context.example def it_raises_the_exception_instance(self): with self.assertRaises(self.exception_class) as cm: self.callable_target(*self.call_args, **self.call_kwargs) self.assertEqual(self.exception, cm.exception) context.nest_context("integration") @context.sub_context(".with_implementation(func)") def with_implementation_func(context): context.memoize("times", lambda _: 3) context.memoize("func_return", lambda _: "mocked response") @context.memoize def func(self): def _func(*args, **kwargs): return self.func_return return _func @context.before def setup_mock(self): self.mock_callable_dsl.with_implementation(self.func) if has_original_callable: context.nest_context("mock call arguments") context.nest_context("assertions") @context.example def it_calls_new_implementation(self): self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), self.func_return, ) @context.sub_context(".with_wrapper(wrapper_func)") def with_wrapper_wrappr_func(context): context.memoize("func_return", lambda _: "mocked response") @context.memoize def wrapper_func(self): def _wrapper_func(original_function, *args, **kwargs): self.assertEqual(original_function, self.original_callable) return self.func_return return _wrapper_func if has_original_callable: context.memoize("times", lambda _: 3) @context.before def setup_mock(self): self.mock_callable_dsl.with_wrapper(self.wrapper_func) context.nest_context("mock call arguments") context.nest_context("assertions") @context.example def it_calls_wrapper_function(self): self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), self.func_return, ) else: @context.example def it_fails_to_mock(self): with self.assertRaisesWithMessage( ValueError, "Can not wrap original callable that does not exist.", ): self.mock_callable_dsl.with_wrapper(self.wrapper_func) @context.sub_context(".to_call_original()") def to_call_original(context): if has_original_callable: context.memoize("times", lambda _: 3) @context.before def setup_mock(self): self.mock_callable_dsl.to_call_original() context.nest_context("mock call arguments") context.nest_context("assertions") @context.example def it_calls_original_implementation(self): self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), self.original_callable(*self.call_args, **self.call_kwargs), ) else: @context.example def it_fails_to_mock(self): with self.assertRaisesWithMessage( ValueError, "Can not call original callable that does not exist.", ): self.mock_callable_dsl.to_call_original() if not callable_accepts_no_args: @context.sub_context def composition(context): """ This context takes care of composition of multiple call/behavior/assertion combination, to ensure they play along well. """ context.memoize( "other_args", lambda self: ["other_arg" for arg in self.call_args]) context.memoize( "other_kwargs", lambda self: {k: "other_value" for k, v in self.call_kwargs.items()}, ) @context.example def newest_mock_has_precedence_over_older_mocks(self): """ Mocks are designed to be composable, allowing us to declare multiple behaviors for different calls. Those definitions stack up, and when a call is made to the mock, they are searched from newest to oldest, to find one that is able to be caled. """ # First, mock all calls mock_callable( self.target_arg, self.callable_arg).to_return_value("any args") # Then we add some specific call behavior mock_callable(self.target_arg, self.callable_arg).for_call( *self.specific_call_args, ** self.specific_call_kwargs).to_return_value("specific") # The first behavior should still be there self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), "any args", ) # as well as the specific case self.assertEqual( self.callable_target(*self.specific_call_args, **self.specific_call_kwargs), "specific", ) # but if we add another "catch all" case mock_callable( self.target_arg, self.callable_arg).to_return_value("new any args") # it should take over any previous mock self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), "new any args", ) self.assertEqual( self.callable_target(*self.specific_call_args, **self.specific_call_kwargs), "new any args", ) @context.sub_context def multiple_assertions(context): @context.before def setup_mocks(self): mock_callable(self.target_arg, self.callable_arg).to_return_value( "any args").and_assert_called_once() mock_callable( self.target_arg, self.callable_arg).for_call( *self.specific_call_args, **self.specific_call_kwargs).to_return_value( "specific").and_assert_called_twice() @context.example def that_passes(self): self.callable_target(*self.other_args, **self.other_kwargs) self.callable_target(*self.specific_call_args, **self.specific_call_kwargs) self.callable_target(*self.specific_call_args, **self.specific_call_kwargs) @context.example def that_fails(self): # "Pass" this test when callable accepts no arguments if (self.specific_call_args == self.call_args and self.specific_call_kwargs == self.call_kwargs): raise RuntimeError("FIXME") return self.callable_target(*self.other_args, **self.other_kwargs) self.callable_target(*self.specific_call_args, **self.specific_call_kwargs) with self.assertRaises(AssertionError): self.assert_all() ## ## Target types ## @context.sub_context def When_target_is_function_of_a_module(context): @context.before def before(self): self.original_callable = testslide._test_function self.real_target = testslide self.target_arg = "testslide" self.callable_arg = "_test_function" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = testslide._test_function context.merge_context("examples for target") @context.example def works_with_alternative_module_names(self): target = "os.path" target_module = os.path alternative_target = "testslide.cli.os.path" import testslide.cli alternative_target_module = testslide.cli.os.path original_function = os.path.exists self.mock_callable( target, "exists").for_call("found").to_return_value(True) self.mock_callable( alternative_target, "exists").for_call("not_found").to_return_value(False) self.assertTrue(target_module.exists("found")) self.assertTrue(alternative_target_module.exists("found")) self.assertFalse(target_module.exists("not_found")) self.assertFalse(alternative_target_module.exists("not_found")) testslide.mock_callable.unpatch_all_callable_mocks() self.assertEqual(os.path.exists, original_function, "Unpatch did not work") @context.sub_context def When_target_is_a_builtin(context): context.memoize("call_args", lambda _: (0, )) context.memoize("call_kwargs", lambda _: {}) context.memoize("specific_call_args", lambda _: (0.000000001, )) context.memoize("specific_call_kwargs", lambda _: {}) @context.before def before(self): self.original_callable = time.sleep self.real_target = time self.target_arg = "time" self.callable_arg = "sleep" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = time.sleep context.merge_context("examples for target", validate_signature=False) @context.sub_context def When_target_is_instance_method_at_a_class(context): @context.example def it_is_not_allowed(self): with self.assertRaises(ValueError): mock_callable(Target, "instance_method") @context.sub_context def When_target_is_class_method_at_a_class(context): @context.before def before(self): self.original_callable = Target.class_method self.real_target = Target self.target_arg = Target self.callable_arg = "class_method" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = Target.class_method context.merge_context("examples for target") @context.sub_context def When_target_is_static_method_at_a_class(context): @context.before def before(self): self.original_callable = Target.static_method self.real_target = Target self.target_arg = Target self.callable_arg = "static_method" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = Target.static_method context.merge_context("examples for target") @context.shared_context def other_instances_are_not_mocked(context): @context.example def other_instances_are_not_mocked(self): mock_callable(self.target_arg, self.callable_arg).to_return_value("mocked value") self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), "mocked value", ) self.assertEqual( getattr(Target(), self.callable_arg)(*self.call_args, **self.call_kwargs), "original response", ) @context.sub_context def When_target_is_instance_method_at_an_instance(context): context.memoize("callable_arg", lambda _: "instance_method") @context.before def before(self): target = Target() self.original_callable = target.instance_method self.real_target = target self.target_arg = target self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = target.instance_method context.merge_context("examples for target") context.merge_context("other instances are not mocked") @context.sub_context def When_target_is_magic_instance_method_at_an_instance(context): context.memoize("call_args", lambda _: ()) context.memoize("call_kwargs", lambda _: {}) @context.shared_context def magic_method_tests(context): @context.before def before(self): self.original_callable = self.target.__str__ self.real_target = self.target self.target_arg = self.target self.callable_arg = "__str__" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = lambda: str(self.target) context.merge_context("examples for target", callable_accepts_no_args=True, can_yield=False) @context.example def other_instances_are_not_mocked(self): mock_callable( self.target_arg, self.callable_arg).to_return_value("mocked value") self.assertEqual(self.callable_target(), "mocked value") self.assertEqual(str(Target()), "original response") @context.sub_context def with_magic_method_defined_on_class(context): context.memoize("target", lambda self: ParentTarget()) context.merge_context("magic method tests") @context.sub_context def with_magic_method_defined_on_parent_class(context): context.memoize("target", lambda self: Target()) context.merge_context("magic method tests") @context.shared_context def class_is_not_mocked(context): @context.example def class_is_not_mocked(self): mock_callable(self.target_arg, self.callable_arg).to_return_value("mocked value") self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), "mocked value", ) self.assertEqual( getattr(Target, self.callable_arg)(*self.call_args, **self.call_kwargs), "original response", ) @context.sub_context def When_target_is_class_method_at_an_instance(context): @context.before def before(self): target = Target() self.original_callable = target.class_method self.real_target = target self.target_arg = target self.callable_arg = "class_method" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = target.class_method context.merge_context("examples for target") context.merge_context("other instances are not mocked") context.merge_context("class is not mocked") @context.sub_context def When_target_is_static_method_at_an_instance(context): @context.before def before(self): target = Target() self.original_callable = target.static_method self.real_target = target self.target_arg = target self.callable_arg = "static_method" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = target.static_method context.merge_context("examples for target") context.merge_context("other instances are not mocked") context.merge_context("class is not mocked") @context.sub_context def When_target_is_a_StrictMock_instance(context): @context.shared_context def other_instances_are_not_mocked(context, runtime_attrs=[]): @context.example def other_instances_are_not_mocked(self): mock_callable( self.target_arg, self.callable_arg).to_return_value("mocked value") self.assertEqual( self.callable_target(*self.call_args, **self.call_kwargs), "mocked value", ) other_strict_mock = StrictMock(template=Target, runtime_attrs=runtime_attrs) mock_callable( other_strict_mock, self.callable_arg).to_return_value("other mocked value") self.assertEqual( getattr(other_strict_mock, self.callable_arg)(*self.call_args, **self.call_kwargs), "other mocked value", ) @context.sub_context def And_attribute_is_a_instance_method(context): context.memoize("callable_arg", lambda _: "instance_method") @context.before def before(self): target = StrictMock(template=Target) self.original_callable = None self.real_target = target self.target_arg = target self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = target.instance_method context.merge_context("examples for target", has_original_callable=False) context.merge_context("other instances are not mocked") @context.sub_context def And_attribute_is_a_magic_instance_method(context): context.memoize("call_args", lambda _: ()) context.memoize("call_kwargs", lambda _: {}) @context.before def before(self): target = StrictMock(template=Target) self.original_callable = None self.real_target = target self.target_arg = target self.callable_arg = "__str__" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = lambda: str(target) context.merge_context( "examples for target", callable_accepts_no_args=True, has_original_callable=False, can_yield=False, ) context.merge_context("other instances are not mocked") @context.sub_context def And_attribute_is_a_dynamic_instance_method(context): context.memoize("callable_arg", lambda _: "dynamic_instance_method") @context.before def before(self): target = StrictMock(template=Target, runtime_attrs=["dynamic_instance_method"]) self.original_callable = None self.real_target = target self.target_arg = target self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = target.dynamic_instance_method context.merge_context("examples for target", has_original_callable=False) context.merge_context( "other instances are not mocked", runtime_attrs=["dynamic_instance_method"], ) @context.sub_context def And_attribute_is_a_class_method(context): @context.before def before(self): target = StrictMock(template=Target) self.original_callable = None self.real_target = target self.target_arg = target self.callable_arg = "class_method" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = target.class_method context.merge_context("examples for target", has_original_callable=False) context.merge_context("other instances are not mocked") context.merge_context("class is not mocked") @context.sub_context def And_attribute_is_a_static_method(context): @context.before def before(self): target = StrictMock(template=Target) self.original_callable = None self.real_target = target self.target_arg = target self.callable_arg = "static_method" self.mock_callable_dsl = mock_callable(self.target_arg, self.callable_arg) self.callable_target = target.static_method context.merge_context("examples for target", has_original_callable=False) context.merge_context("other instances are not mocked") context.merge_context("class is not mocked")
def patch_attribute_tests(context): ## ## Attributes ## context.memoize("new_value", lambda self: "new_value") ## ## Functions ## ## ## Hooks ## ## ## Shared Contexts ## @context.shared_context def patching_works(context): @context.example def patching_works(self): def sm_hasattr(obj, name): try: return hasattr(obj, name) except UndefinedAttribute: return False if sm_hasattr(self.real_target, self.attribute): original_value = getattr(self.real_target, self.attribute) else: original_value = None self.assertNotEqual(original_value, self.new_value) self.patch_attribute(self.target, self.attribute, self.new_value) self.assertEqual(getattr(self.real_target, self.attribute), self.new_value) unpatch_all_mocked_attributes() if original_value: self.assertEqual( getattr(self.real_target, self.attribute), original_value ) else: self.assertFalse(sm_hasattr(self.real_target, self.attribute)) @context.shared_context def common(context, fails_if_class_attribute): context.merge_context("patching works") @context.example def it_fails_if_attribute_is_callable(self): with self.assertRaisesRegex(ValueError, "^Attribute can not be callable*"): self.patch_attribute( self.target, self.callable_attribute, self.new_value ) if fails_if_class_attribute: @context.example def it_fails_if_attribute_is_a_class(self): with self.assertRaisesRegex( ValueError, "^Attribute can not be a class*" ): self.patch_attribute( self.target, self.class_attribute, self.new_value ) else: @context.sub_context def with_class_attributes(context): context.merge_context("patching works") @context.xexample def it_fails_if_new_value_is_of_incompatible_type(self): pass ## ## Contexts ## @context.sub_context def when_target_is_a_module(context): context.memoize("callable_attribute", lambda self: "test_function") context.memoize("class_attribute", lambda self: "SomeClass") context.memoize("attribute", lambda self: "attribute") @context.sub_context def given_as_a_reference(context): context.memoize("target", lambda self: sample_module) context.memoize("real_target", lambda self: self.target) context.merge_context("common", fails_if_class_attribute=True) @context.sub_context def given_as_a_string(context): context.memoize("target", lambda self: "tests.sample_module") context.memoize("real_target", lambda self: sample_module) context.merge_context("common", fails_if_class_attribute=True) @context.sub_context def when_target_is_a_class(context): context.memoize("target", lambda self: sample_module.SomeClass) context.memoize("real_target", lambda self: self.target) context.memoize("callable_attribute", lambda self: "method") context.memoize("class_attribute", lambda self: "other_class_attribute") context.memoize("attribute", lambda self: "attribute") context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def when_target_is_an_instance(context): context.memoize("target", lambda self: sample_module.SomeClass()) context.memoize("real_target", lambda self: self.target) context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def and_attribute_is_a_property(context): context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def when_target_is_a_StrictMock(context): context.memoize("real_target", lambda self: self.target) context.memoize("callable_attribute", lambda self: "method") context.memoize("class_attribute", lambda self: "other_class_attribute") context.memoize("attribute", lambda self: "attribute") @context.sub_context def with_a_template(context): context.memoize( "target", lambda self: StrictMock(template=sample_module.SomeClass) ) context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def without_a_template(context): context.memoize("target", lambda self: StrictMock()) context.merge_context("patching works") @context.example def patch_attribute_raises_valueerror_for_private(self): with self.assertRaises(ValueError): self.patch_attribute( sample_module.SomeClass, "_private_attr", "notsoprivate" ) @context.example def patch_attribute_passes_for_private_with_allow_private(self): self.patch_attribute( sample_module.SomeClass, "_private_attr", "notsoprivate", allow_private=True )
def without_a_template(context): context.memoize("target", lambda self: StrictMock()) context.merge_context("patching works")
def with_a_template(context): context.memoize( "target", lambda self: StrictMock(template=sample_module.SomeClass) ) context.merge_context("common", fails_if_class_attribute=False)
def given_as_a_string(context): context.memoize("target", lambda self: "tests.sample_module") context.memoize("real_target", lambda self: sample_module) context.merge_context("common", fails_if_class_attribute=True)
def given_as_a_reference(context): context.memoize("target", lambda self: sample_module) context.memoize("real_target", lambda self: self.target) context.merge_context("common", fails_if_class_attribute=True)
def and_attribute_is_a_property(context): context.memoize("attribute", lambda self: "property_attribute") context.merge_context("common", fails_if_class_attribute=False)
def patch_attribute_tests(context): ## ## Attributes ## context.memoize("new_value", lambda self: "new_value") ## ## Functions ## ## ## Hooks ## ## ## Shared Contexts ## @context.shared_context def patching_works(context): @context.function def strict_mock_hasattr(self, obj, name): try: return hasattr(obj, name) except UndefinedAttribute: return False @context.before def before(self): if self.strict_mock_hasattr(self.real_target, self.attribute): self.original_value = getattr(self.real_target, self.attribute) else: self.original_value = None self.assertNotEqual( self.original_value, self.new_value, "Previous test tainted this result!", ) @context.after def after(self): self.assertEqual( getattr(self.real_target, self.attribute), self.new_value, "Patching did not work", ) unpatch_all_mocked_attributes() if self.original_value: self.assertEqual( getattr(self.real_target, self.attribute), self.original_value, "Unpatching did not work.", ) else: self.assertFalse( self.strict_mock_hasattr(self.real_target, self.attribute), "Unpatching did not work", ) @context.example def patching_works(self): self.patch_attribute(self.target, self.attribute, self.new_value) @context.example def double_patching_works(self): self.patch_attribute(self.target, self.attribute, "whatever") self.patch_attribute(self.target, self.attribute, self.new_value) @context.shared_context def common(context, fails_if_class_attribute): context.nest_context("patching works") @context.example def it_fails_if_attribute_is_callable(self): with self.assertRaisesRegex(ValueError, "^Attribute can not be callable*"): self.patch_attribute(self.target, self.callable_attribute, self.new_value) if fails_if_class_attribute: @context.example def it_fails_if_attribute_is_a_class(self): with self.assertRaisesRegex(ValueError, "^Attribute can not be a class*"): self.patch_attribute(self.target, self.class_attribute, self.new_value) else: @context.sub_context def with_class_attributes(context): context.merge_context("patching works") @context.sub_context def type_validation(context): @context.example def it_fails_if_new_value_is_of_incompatible_type(self): with self.assertRaises(TypeCheckError): self.patch_attribute(self.target, "typedattr", 123) @context.example def it_passes_if_new_value_is_of_incompatible_type_with_type_validation_false( self, ): self.patch_attribute(self.target, "typedattr", 123, type_validation=False) @context.example def it_passes_if_new_value_is_of_matching_type(self, ): self.patch_attribute(self.target, "typedattr", "mocked") ## ## Contexts ## @context.sub_context def when_target_is_a_module(context): context.memoize("callable_attribute", lambda self: "test_function") context.memoize("class_attribute", lambda self: "SomeClass") context.memoize("attribute", lambda self: "attribute") @context.sub_context def given_as_a_reference(context): context.memoize("target", lambda self: sample_module) context.memoize("real_target", lambda self: self.target) context.merge_context("common", fails_if_class_attribute=True) @context.sub_context def given_as_a_string(context): context.memoize("target", lambda self: "tests.sample_module") context.memoize("real_target", lambda self: sample_module) context.merge_context("common", fails_if_class_attribute=True) @context.sub_context def when_target_is_a_class(context): context.memoize("target", lambda self: sample_module.SomeClass) context.memoize("real_target", lambda self: self.target) context.memoize("callable_attribute", lambda self: "method") context.memoize("class_attribute", lambda self: "other_class_attribute") context.memoize("attribute", lambda self: "attribute") context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def when_target_is_an_instance(context): context.memoize("target", lambda self: sample_module.SomeClass()) context.memoize("real_target", lambda self: self.target) context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def and_attribute_is_a_property(context): context.memoize("attribute", lambda self: "property_attribute") context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def when_target_is_a_StrictMock(context): context.memoize("real_target", lambda self: self.target) context.memoize("callable_attribute", lambda self: "method") context.memoize("class_attribute", lambda self: "other_class_attribute") context.memoize("attribute", lambda self: "attribute") @context.sub_context def with_a_template(context): context.memoize( "target", lambda self: StrictMock(template=sample_module.SomeClass)) context.merge_context("common", fails_if_class_attribute=False) @context.sub_context def without_a_template(context): context.memoize("target", lambda self: StrictMock()) context.merge_context("patching works") @context.example def patch_attribute_raises_valueerror_for_private(self): with self.assertRaises(ValueError): self.patch_attribute(sample_module.SomeClass, "_private_attr", "notsoprivate") @context.example def patch_attribute_passes_for_private_with_allow_private(self): self.patch_attribute(sample_module.SomeClass, "_private_attr", "notsoprivate", allow_private=True)