diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1e89d8d..93f960d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -29,7 +29,7 @@ jobs: - name: Generate report run: | - poetry run coverage run --parallel-mode --source flexmock -m unittest tests/test_flexmock.py + export PYTHONPATH=$PWD poetry run coverage run --parallel-mode --source flexmock tests/test_unittest.py poetry run coverage run --parallel-mode --source flexmock tests/test_doctest.py poetry run coverage run --parallel-mode --source flexmock -m pytest tests/test_pytest.py diff --git a/Makefile b/Makefile index 2a85147..1f285be 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ unittest: @printf '\n\n*****************\n' @printf '$(color)Running unittest$(off)\n' @printf '*****************\n' - $(PYTHON) -m unittest tests/test_flexmock.py + $(PYTHON) tests/test_unittest.py .PHONY: doctest doctest: @@ -51,7 +51,7 @@ twisted: @printf '\n\n*****************\n' @printf '$(color)Running twisted tests$(off)\n' @printf '*****************\n' - $(PYTHON) -c "from twisted.scripts.trial import run; run();" tests/test_flexmock.py + $(PYTHON) -c "from twisted.scripts.trial import run; run();" tests/test_integrations.py .PHONY: mypy mypy: diff --git a/tests/features/__init__.py b/tests/features/__init__.py new file mode 100644 index 0000000..a65281b --- /dev/null +++ b/tests/features/__init__.py @@ -0,0 +1,33 @@ +"""Tests for flexmock. + +Tests under this module should not depend on any test runner. +""" +from .arg_matching import ArgumentMatchingTestCase +from .call_count import CallCountTestCase +from .common import CommonTestCase +from .conditional import ConditionalAssertionsTestCase +from .derived import DerivedTestCase +from .mocking import MockingTestCase +from .ordered import OrderedTestCase +from .proxied import ProxiedTestCase +from .spying import SpyingTestCase +from .stubbing import StubbingTestCase +from .teardown import TeardownTestCase + + +class FlexmockTestCase( # pylint: disable=too-many-ancestors + ArgumentMatchingTestCase, + CallCountTestCase, + CommonTestCase, + ConditionalAssertionsTestCase, + DerivedTestCase, + MockingTestCase, + OrderedTestCase, + ProxiedTestCase, + SpyingTestCase, + StubbingTestCase, + TeardownTestCase, +): + """This class should be imported by other test modules to run full flexmock + test suite. + """ diff --git a/tests/features/arg_matching.py b/tests/features/arg_matching.py new file mode 100644 index 0000000..973b31a --- /dev/null +++ b/tests/features/arg_matching.py @@ -0,0 +1,255 @@ +"""Tests for argument matching.""" +# pylint: disable=missing-docstring,no-self-use,no-member +import re + +from flexmock import exceptions, flexmock +from flexmock._api import flexmock_teardown +from tests import some_module +from tests.some_module import SomeClass +from tests.utils import assert_raises + + +class ArgumentMatchingTestCase: + def test_arg_matching_works_with_regexp(self): + class Foo: + def method(self, arg1, arg2): + pass + + instance = Foo() + flexmock(instance).should_receive("method").with_args( + re.compile("^arg1.*asdf$"), arg2=re.compile("f") + ).and_return("mocked") + assert instance.method("arg1somejunkasdf", arg2="aadsfdas") == "mocked" + + def test_arg_matching_with_regexp_fails_when_regexp_doesnt_match_karg(self): + class Foo: + def method(self, arg1, arg2): + pass + + instance = Foo() + flexmock(instance).should_receive("method").with_args( + re.compile("^arg1.*asdf$"), arg2=re.compile("a") + ).and_return("mocked") + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method did not match expectations:\n" + ' Received call:\tmethod("arg1somejunkasdfa", arg2="a")\n' + " Expected call[1]:\tmethod(arg2=/a/, arg1=/^arg1.*asdf$/)" + ), + ): + instance.method("arg1somejunkasdfa", arg2="a") + + def test_arg_matching_with_regexp_fails_when_regexp_doesnt_match_kwarg(self): + class Foo: + def method(self, arg1, arg2): + pass + + instance = Foo() + flexmock(instance).should_receive("method").with_args( + re.compile("^arg1.*asdf$"), arg2=re.compile("a") + ).and_return("mocked") + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method did not match expectations:\n" + ' Received call:\tmethod("arg1somejunkasdf", arg2="b")\n' + " Expected call[1]:\tmethod(arg2=/a/, arg1=/^arg1.*asdf$/)" + ), + ): + instance.method("arg1somejunkasdf", arg2="b") + + def test_module_level_function_with_kwargs(self): + flexmock(some_module).should_receive("module_function").with_args(1, y="expected") + with assert_raises( + exceptions.FlexmockError, + ( + "Arguments for call module_function did not match expectations:\n" + ' Received call:\tmodule_function(1, y="not expected")\n' + ' Expected call[1]:\tmodule_function(y="expected", x=1)' + ), + ): + some_module.module_function(1, y="not expected") + + def test_flexmock_should_match_types_on_multiple_arguments(self): + class Foo: + def method1(self, arg1, arg2): + pass + + instance = Foo() + flexmock(instance).should_receive("method1").with_args(str, int).and_return("ok") + assert instance.method1("some string", 12) == "ok" + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + " Received call:\tmethod1(12, 32)\n" + " Expected call[1]:\tmethod1(arg1=, arg2=)" + ), + ): + instance.method1(12, 32) + flexmock(instance).should_receive("method1").with_args(str, int).and_return("ok") + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + ' Received call:\tmethod1(12, "some string")\n' + " Expected call[1]:\tmethod1(arg1=, arg2=)\n" + " Expected call[2]:\tmethod1(arg1=, arg2=)" + ), + ): + instance.method1(12, "some string") + flexmock(instance).should_receive("method1").with_args(str, int).and_return("ok") + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + ' Received call:\tmethod1("string", 12, 14)\n' + " Expected call[1]:\tmethod1(arg1=, arg2=)\n" + " Expected call[2]:\tmethod1(arg1=, arg2=)\n" + " Expected call[3]:\tmethod1(arg1=, arg2=)" + ), + ): + instance.method1("string", 12, 14) + + def test_flexmock_should_match_types_on_multiple_arguments_generic(self): + class Foo: + def method1(self, a, b, c): # pylint: disable=invalid-name + pass + + instance = Foo() + flexmock(instance).should_receive("method1").with_args(object, object, object).and_return( + "ok" + ) + assert instance.method1("some string", None, 12) == "ok" + assert instance.method1((1,), None, 12) == "ok" + assert instance.method1(12, 14, []) == "ok" + assert instance.method1("some string", "another one", False) == "ok" + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + ' Received call:\tmethod1("string", 12)\n' + " Expected call[1]:\tmethod1(a=, " + "b=, c=)" + ), + ): + instance.method1("string", 12) # pylint: disable=no-value-for-parameter + flexmock_teardown() + flexmock(instance).should_receive("method1").with_args(object, object, object).and_return( + "ok" + ) + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + ' Received call:\tmethod1("string", 12, 13, 14)\n' + " Expected call[1]:\tmethod1(a=, " + "b=, c=)" + ), + ): + instance.method1("string", 12, 13, 14) + + def test_flexmock_should_match_types_on_multiple_arguments_classes(self): + class Foo: + def method1(self, a, b): # pylint: disable=invalid-name + pass + + class Bar: + pass + + foo_instance = Foo() + bar_instance = Bar() + flexmock(foo_instance).should_receive("method1").with_args(object, Bar).and_return("ok") + assert foo_instance.method1("some string", bar_instance) == "ok" + with assert_raises( + exceptions.MethodSignatureError, + re.compile( + "Arguments for call method1 did not match expectations:\n" + r' Received call:\tmethod1\(.+\.\.Bar object at 0x.+>, "some string"\)\n' + r" Expected call\[1\]:\tmethod1\(a=, b=\.Bar'>\)" + ), + ): + foo_instance.method1(bar_instance, "some string") + flexmock_teardown() + flexmock(foo_instance).should_receive("method1").with_args(object, Bar).and_return("ok") + with assert_raises( + exceptions.MethodSignatureError, + re.compile( + "Arguments for call method1 did not match expectations:\n" + r' Received call:\tmethod1\(12, "some string"\)\n' + r" Expected call\[1\]:\tmethod1\(a=, b=\.Bar'>\)" + ), + ): + foo_instance.method1(12, "some string") + + def test_flexmock_should_match_keyword_arguments(self): + class Foo: + def method1(self, arg1, **kwargs): + pass + + instance = Foo() + flexmock(instance).should_receive("method1").with_args(1, arg3=3, arg2=2).twice() + instance.method1(1, arg2=2, arg3=3) + instance.method1(1, arg3=3, arg2=2) + flexmock_teardown() + flexmock(instance).should_receive("method1").with_args(1, arg3=3, arg2=2) + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + " Received call:\tmethod1(arg2=2, arg3=3)\n" + " Expected call[1]:\tmethod1(arg3=3, arg2=2, arg1=1)" + ), + ): + instance.method1(arg2=2, arg3=3) # pylint: disable=no-value-for-parameter + flexmock_teardown() + flexmock(instance).should_receive("method1").with_args(1, arg3=3, arg2=2) + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + " Received call:\tmethod1(1, arg2=2, arg3=4)\n" + " Expected call[1]:\tmethod1(arg3=3, arg2=2, arg1=1)" + ), + ): + instance.method1(1, arg2=2, arg3=4) + flexmock_teardown() + flexmock(instance).should_receive("method1").with_args(1, arg3=3, arg2=2) + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method1 did not match expectations:\n" + " Received call:\tmethod1(1)\n" + " Expected call[1]:\tmethod1(arg3=3, arg2=2, arg1=1)" + ), + ): + instance.method1(1) + + def test_flexmock_should_call_should_match_keyword_arguments(self): + class Foo: + def method1(self, arg1, arg2=None, arg3=None): + return f"{arg1}{arg2}{arg3}" + + instance = Foo() + flexmock(instance).should_call("method1").with_args(1, arg3=3, arg2=2).once() + assert instance.method1(1, arg2=2, arg3=3) == "123" + + def test_with_args_with_instance_method(self): + flexmock(SomeClass).should_receive("instance_method_with_args").with_args("red").once() + flexmock(SomeClass).should_receive("instance_method_with_args").with_args("blue").once() + instance = SomeClass() + instance.instance_method_with_args("red") + instance.instance_method_with_args("blue") + + def test_with_args_with_class_method(self): + flexmock(SomeClass).should_receive("class_method_with_args").with_args("red").once() + flexmock(SomeClass).should_receive("class_method_with_args").with_args("blue").once() + SomeClass.class_method_with_args("red") + SomeClass.class_method_with_args("blue") + + def test_with_args_with_static_method(self): + flexmock(SomeClass).should_receive("static_method_with_args").with_args("red").once() + flexmock(SomeClass).should_receive("static_method_with_args").with_args("blue").once() + SomeClass.static_method_with_args("red") + SomeClass.static_method_with_args("blue") diff --git a/tests/features/call_count.py b/tests/features/call_count.py new file mode 100644 index 0000000..1991918 --- /dev/null +++ b/tests/features/call_count.py @@ -0,0 +1,75 @@ +"""Tests for call count modifiers.""" +# pylint: disable=missing-docstring,no-self-use,no-member + +from flexmock import exceptions, flexmock +from flexmock._api import flexmock_teardown +from tests.utils import assert_raises + + +class CallCountTestCase: + def test_support_at_least_and_at_most_together(self): + class Foo: + def method(self): + pass + + instance = Foo() + flexmock(instance).should_call("method").at_least().once().at_most().twice() + with assert_raises( + exceptions.MethodCallError, + "method() expected to be called at least 1 time and at most 2 times, called 0 times", + ): + flexmock_teardown() + + flexmock(instance).should_call("method").at_least().once().at_most().twice() + instance.method() + instance.method() + with assert_raises( + exceptions.MethodCallError, + "method() expected to be called at most 2 times, called 3 times", + ): + instance.method() + + flexmock(instance).should_call("method").at_least().once().at_most().twice() + instance.method() + flexmock_teardown() + + flexmock(instance).should_call("method").at_least().once().at_most().twice() + instance.method() + instance.method() + flexmock_teardown() + + def test_at_least_cannot_be_used_twice(self): + class Foo: + def method(self): + pass + + expectation = flexmock(Foo).should_receive("method") + with assert_raises(exceptions.FlexmockError, "cannot use at_least modifier twice"): + expectation.at_least().at_least() + + def test_at_most_cannot_be_used_twice(self): + class Foo: + def method(self): + pass + + expectation = flexmock(Foo).should_receive("method") + with assert_raises(exceptions.FlexmockError, "cannot use at_most modifier twice"): + expectation.at_most().at_most() + + def test_at_least_cannot_be_specified_until_at_most_is_set(self): + class Foo: + def method(self): + pass + + expectation = flexmock(Foo).should_receive("method") + with assert_raises(exceptions.FlexmockError, "cannot use at_most with at_least unset"): + expectation.at_least().at_most() + + def test_at_most_cannot_be_specified_until_at_least_is_set(self): + class Foo: + def method(self): + pass + + expectation = flexmock(Foo).should_receive("method") + with assert_raises(exceptions.FlexmockError, "cannot use at_least with at_most unset"): + expectation.at_most().at_least() diff --git a/tests/features/common.py b/tests/features/common.py new file mode 100644 index 0000000..fb95985 --- /dev/null +++ b/tests/features/common.py @@ -0,0 +1,156 @@ +"""Flexmock tests that are not related to any particular feature.""" +# pylint: disable=missing-docstring,no-self-use +from flexmock import Mock, flexmock +from flexmock._api import ( + ReturnValue, + _format_args, + _is_class_method, + _is_static_method, + _isproperty, +) +from tests.proxy import Proxy +from tests.some_module import DerivedClass, SomeClass + + +class CommonTestCase: + def test_return_value_to_str(self): + r_val = ReturnValue(value=1) + assert str(r_val) == "1" + + r_val = ReturnValue(value=(1,)) + assert str(r_val) == "1" + + r_val = ReturnValue(value=(1, 2)) + assert str(r_val) == "(1, 2)" + + r_val = ReturnValue(value=1, raises=RuntimeError) + assert str(r_val) == "(1)" + + def test_is_class_method(self): + assert _is_class_method(SomeClass.class_method, "class_method") is True + assert _is_class_method(SomeClass.static_method, "static_method") is False + assert _is_class_method(SomeClass.instance_method, "instance_method") is False + # Method names do no match + assert _is_class_method(SomeClass.class_method, "other_method") is False + + some_class = SomeClass() + assert _is_class_method(some_class.class_method, "class_method") is True + assert _is_class_method(some_class.static_method, "static_method") is False + assert _is_class_method(some_class.instance_method, "instance_method") is False + + assert _is_class_method(DerivedClass.class_method, "class_method") is True + assert _is_class_method(DerivedClass.static_method, "static_method") is False + assert _is_class_method(DerivedClass.instance_method, "instance_method") is False + + derived_class = DerivedClass() + assert _is_class_method(derived_class.class_method, "class_method") is True + assert _is_class_method(derived_class.static_method, "static_method") is False + assert _is_class_method(derived_class.instance_method, "instance_method") is False + + def test_is_class_method_proxied(self): + # pylint: disable=not-callable,invalid-name + SomeClassProxy = Proxy(SomeClass) + assert _is_class_method(SomeClassProxy.class_method, "class_method") is True + assert _is_class_method(SomeClassProxy.static_method, "static_method") is False + assert _is_class_method(SomeClassProxy.instance_method, "instance_method") is False + + some_class = SomeClassProxy() + assert _is_class_method(some_class.class_method, "class_method") is True + assert _is_class_method(some_class.static_method, "static_method") is False + assert _is_class_method(some_class.instance_method, "instance_method") is False + + DerivedClassProxy = Proxy(DerivedClass) + assert _is_class_method(DerivedClassProxy.class_method, "class_method") is True + assert _is_class_method(DerivedClassProxy.static_method, "static_method") is False + assert _is_class_method(DerivedClassProxy.instance_method, "instance_method") is False + + derived_class = DerivedClassProxy() + assert _is_class_method(derived_class.class_method, "class_method") is True + assert _is_class_method(derived_class.static_method, "static_method") is False + assert _is_class_method(derived_class.instance_method, "instance_method") is False + + def test_is_static_method(self): + assert _is_static_method(SomeClass, "class_method") is False + assert _is_static_method(SomeClass, "static_method") is True + assert _is_static_method(SomeClass, "instance_method") is False + + some_class = SomeClass() + assert _is_static_method(some_class, "class_method") is False + assert _is_static_method(some_class, "static_method") is True + assert _is_static_method(some_class, "instance_method") is False + + assert _is_static_method(DerivedClass, "class_method") is False + assert _is_static_method(DerivedClass, "static_method") is True + assert _is_static_method(DerivedClass, "instance_method") is False + + derived_class = DerivedClass() + assert _is_static_method(derived_class, "class_method") is False + assert _is_static_method(derived_class, "static_method") is True + assert _is_static_method(derived_class, "instance_method") is False + + def test_is_static_method_proxied(self): + # pylint: disable=not-callable,invalid-name + SomeClassProxy = Proxy(SomeClass) + assert _is_static_method(SomeClassProxy, "class_method") is False + assert _is_static_method(SomeClassProxy, "static_method") is True + assert _is_static_method(SomeClassProxy, "instance_method") is False + + some_class = SomeClassProxy() + assert _is_static_method(some_class, "class_method") is False + assert _is_static_method(some_class, "static_method") is True + assert _is_static_method(some_class, "instance_method") is False + + DerivedClassProxy = Proxy(DerivedClass) + assert _is_static_method(DerivedClassProxy, "class_method") is False + assert _is_static_method(DerivedClassProxy, "static_method") is True + assert _is_static_method(DerivedClassProxy, "instance_method") is False + + derived_class = DerivedClassProxy() + assert _is_static_method(derived_class, "class_method") is False + assert _is_static_method(derived_class, "static_method") is True + assert _is_static_method(derived_class, "instance_method") is False + + def test_isproperty(self): + class BaseClass: + @property + def method1(self): + pass + + def method2(self): + pass + + class ChildClass(BaseClass): + pass + + base_instance = BaseClass() + child_instance = ChildClass() + assert _isproperty(base_instance, "method1") is True + assert _isproperty(base_instance, "method2") is False + assert _isproperty(BaseClass, "method1") is True + assert _isproperty(BaseClass, "method2") is False + assert _isproperty(child_instance, "method1") is True + assert _isproperty(child_instance, "method2") is False + assert _isproperty(ChildClass, "method1") is True + assert _isproperty(ChildClass, "method2") is False + assert _isproperty(Mock(), "method2") is False + + def test_format_args_supports_tuples(self): + formatted = _format_args("method", {"kargs": ((1, 2),), "kwargs": {}}) + assert formatted == "method((1, 2))" + + def test_flexmock_should_not_explode_on_unicode_formatting(self): + formatted = _format_args("method", {"kargs": (chr(0x86C7),), "kwargs": {}}) + assert formatted == 'method("蛇")' + + def test_return_value_should_not_explode_on_unicode_values(self): + return_value = ReturnValue(chr(0x86C7)) + assert f"{return_value}" == '"蛇"' + return_value = ReturnValue((chr(0x86C7), chr(0x86C7))) + assert f"{return_value}" == '("蛇", "蛇")' + + def test_flexmock_should_yield_self_when_iterated(self): + class ClassNoIter: + pass + + instance = flexmock(ClassNoIter) + assert instance is list(instance)[0] diff --git a/tests/features/conditional.py b/tests/features/conditional.py new file mode 100644 index 0000000..d7cb8f7 --- /dev/null +++ b/tests/features/conditional.py @@ -0,0 +1,63 @@ +"""Tests for conditional assertions.""" +# pylint: disable=missing-docstring,no-self-use,no-member +import functools + +from flexmock import exceptions, flexmock +from tests.utils import assert_raises + + +class ConditionalAssertionsTestCase: + def test_state_machine(self): + class Radio: + def __init__(self): + self.is_on = False + self.volume = 0 + + def switch_on(self): + self.is_on = True + + def switch_off(self): + self.is_on = False + + def select_channel(self): + return None + + def adjust_volume(self, num): + self.volume = num + + radio = Radio() + flexmock(radio) + + def radio_is_on(): + return radio.is_on + + radio.should_receive("select_channel").once().when(lambda: radio.is_on) + radio.should_call("adjust_volume").once().with_args(5).when(radio_is_on) + + with assert_raises( + exceptions.StateError, + "select_channel expected to be called when lambda: radio.is_on is True", + ): + radio.select_channel() + with assert_raises( + exceptions.StateError, "adjust_volume expected to be called when radio_is_on is True" + ): + radio.adjust_volume(5) + radio.is_on = True + radio.select_channel() + radio.adjust_volume(5) + + def test_when_parameter_should_be_callable(self): + with assert_raises(exceptions.FlexmockError, "when() parameter must be callable"): + flexmock().should_receive("something").when(1) + + def test_flexmock_should_not_blow_up_with_builtin_in_when(self): + # It is not possible to get source for builtins. Flexmock should handle + # this gracefully. + mock = flexmock() + mock.should_receive("something").when(functools.partial(lambda: False)) + with assert_raises( + exceptions.StateError, "something expected to be called when condition is True" + ): + # Should not raise TypeError + mock.something() diff --git a/tests/features/derived.py b/tests/features/derived.py new file mode 100644 index 0000000..d0158d3 --- /dev/null +++ b/tests/features/derived.py @@ -0,0 +1,129 @@ +"""Tests for mocking and spying derived classes.""" +# pylint: disable=missing-docstring,no-self-use,no-member +from flexmock import exceptions, flexmock +from tests.some_module import DerivedClass, SomeClass +from tests.utils import assert_raises + + +class DerivedTestCase: + def test_mock_class_method_on_derived_class(self): + flexmock(DerivedClass).should_receive("class_method").and_return(2).twice() + assert DerivedClass().class_method() == 2 + assert DerivedClass.class_method() == 2 + + def test_mock_class_method_on_derived_class_after_mocking_base_class(self): + flexmock(SomeClass).should_receive("class_method").and_return(1).once() + assert SomeClass.class_method() == 1 + flexmock(DerivedClass).should_receive("class_method").and_return(2).twice() + assert DerivedClass().class_method() == 2 + assert DerivedClass.class_method() == 2 + + def test_mock_static_method_on_derived_class(self): + flexmock(DerivedClass).should_receive("static_method").and_return(4).twice() + assert DerivedClass().static_method() == 4 + assert DerivedClass.static_method() == 4 + + def test_mock_static_method_on_derived_class_after_mocking_base_class(self): + flexmock(SomeClass).should_receive("static_method").and_return(3).once() + assert SomeClass.static_method() == 3 + flexmock(DerivedClass).should_receive("static_method").and_return(4).twice() + assert DerivedClass().static_method() == 4 + assert DerivedClass.static_method() == 4 + + def test_mock_class_method_with_args_on_derived_class(self): + flexmock(DerivedClass).should_receive("class_method_with_args").with_args(2).and_return( + 3 + ).twice() + assert DerivedClass().class_method_with_args(2) == 3 + assert DerivedClass.class_method_with_args(2) == 3 + + def test_mock_class_method_with_args_on_derived_class_after_mocking_base_class(self): + flexmock(SomeClass).should_receive("class_method_with_args").with_args(1).and_return( + 2 + ).once() + assert SomeClass.class_method_with_args(1) == 2 + flexmock(DerivedClass).should_receive("class_method_with_args").with_args(2).and_return( + 3 + ).twice() + assert DerivedClass().class_method_with_args(2) == 3 + assert DerivedClass.class_method_with_args(2) == 3 + + def test_mock_static_method_with_args_on_derived_class(self): + flexmock(DerivedClass).should_receive("static_method_with_args").with_args(4).and_return( + 5 + ).twice() + assert DerivedClass().static_method_with_args(4) == 5 + assert DerivedClass.static_method_with_args(4) == 5 + + def test_mock_static_method_with_args_on_derived_class_after_mocking_base_class(self): + flexmock(SomeClass).should_receive("static_method_with_args").with_args(2).and_return( + 3 + ).once() + assert SomeClass.static_method_with_args(2) == 3 + flexmock(DerivedClass).should_receive("static_method_with_args").with_args(4).and_return( + 5 + ).twice() + assert DerivedClass().static_method_with_args(4) == 5 + assert DerivedClass.static_method_with_args(4) == 5 + + def test_spy_class_method_on_derived_class(self): + flexmock(DerivedClass).should_call("class_method").and_return("class_method").twice() + assert DerivedClass().class_method() == "class_method" + assert DerivedClass.class_method() == "class_method" + + def test_spy_class_method_on_derived_class_after_spying_base_class(self): + flexmock(SomeClass).should_call("class_method").and_return("class_method").times( + 3 + ) # TODO: Should be once #80 + assert SomeClass.class_method() == "class_method" + flexmock(DerivedClass).should_call("class_method").and_return("class_method").twice() + assert DerivedClass().class_method() == "class_method" + assert DerivedClass.class_method() == "class_method" + + def test_spy_static_method_on_derived_class(self): + flexmock(DerivedClass).should_call("static_method").and_return("static_method").twice() + assert DerivedClass().static_method() == "static_method" + assert DerivedClass.static_method() == "static_method" + + def test_spy_static_method_on_derived_class_after_spying_base_class(self): + flexmock(SomeClass).should_call("static_method").and_return("static_method").times( + 3 + ) # TODO: Should be once #80 + assert SomeClass.static_method() == "static_method" + flexmock(DerivedClass).should_call("static_method").and_return("static_method").twice() + assert DerivedClass().static_method() == "static_method" + assert DerivedClass.static_method() == "static_method" + + def test_spy_class_method_with_args_on_derived_class(self): + flexmock(DerivedClass).should_call("class_method_with_args").with_args(2).and_return(2) + assert DerivedClass().class_method_with_args(2) == 2 + assert DerivedClass.class_method_with_args(2) == 2 + + @assert_raises( + exceptions.MethodSignatureError, match=None + ) # TODO: Should not raise exception #79 + def test_spy_class_method_with_args_on_derived_class_after_spying_base_class(self): + flexmock(SomeClass).should_call("class_method_with_args").with_args(1).and_return(1) + assert SomeClass.class_method_with_args(1) == 1 + flexmock(DerivedClass).should_call("class_method_with_args").with_args(2).and_return(2) + assert DerivedClass().class_method_with_args(2) == 2 + assert DerivedClass.class_method_with_args(2) == 2 + + def test_spy_static_method_with_args_on_derived_class(self): + flexmock(DerivedClass).should_call("static_method_with_args").with_args(4).and_return( + 4 + ).twice() + assert DerivedClass().static_method_with_args(4) == 4 + assert DerivedClass.static_method_with_args(4) == 4 + + @assert_raises( + exceptions.MethodSignatureError, match=None + ) # TODO: Should not raise exception #79 + def test_spy_static_method_with_args_on_derived_class_after_spying_base_class(self): + flexmock(SomeClass).should_call("static_method_with_args").with_args(2).and_return(2).once() + assert SomeClass.static_method_with_args(2) == 2 + flexmock(DerivedClass).should_call("static_method_with_args").with_args(4).and_return( + 4 + ).once() # should be twice + assert DerivedClass().static_method_with_args(4) == 4 + assert DerivedClass.static_method_with_args(4) == 4 diff --git a/tests/features/mocking.py b/tests/features/mocking.py new file mode 100644 index 0000000..d578391 --- /dev/null +++ b/tests/features/mocking.py @@ -0,0 +1,1404 @@ +"""Tests for flexmock mocking feature.""" +# pylint: disable=missing-docstring,no-self-use,no-member,too-many-lines +import os +import random +import re +import sys + +from flexmock import exceptions, flexmock +from flexmock._api import AT_LEAST, AT_MOST, EXACTLY, FlexmockContainer, Mock, flexmock_teardown +from tests import some_module +from tests.utils import assert_raises + + +class MockingTestCase: + def test_print_expectation(self): + mock = flexmock() + expectation = mock.should_receive("foo") + assert str(expectation) == "foo() -> ()" + + def test_flexmock_should_create_mock_object(self): + mock = flexmock() + assert isinstance(mock, Mock) + + def test_flexmock_should_create_mock_object_from_dict(self): + mock = flexmock(foo="foo", bar="bar") + assert mock.foo == "foo" + assert mock.bar == "bar" + + def test_flexmock_should_add_expectations(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo") + assert "method_foo" in [x._name for x in FlexmockContainer.flexmock_objects[mock]] + + def test_flexmock_should_return_value(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar") + mock.should_receive("method_bar").and_return("value_baz") + assert mock.method_foo() == "value_bar" + assert mock.method_bar() == "value_baz" + + def test_calling_deprecated_properties_should_raise_exception(self): + error_msg = "Calling once, twice, never, or mock without parentheses has been deprecated" + mock = flexmock() + mock.should_receive("foo").once # pylint: disable=expression-not-assigned + with assert_raises(exceptions.FlexmockError, error_msg): + flexmock_teardown() + mock.should_receive("foo").twice # pylint: disable=expression-not-assigned + with assert_raises(exceptions.FlexmockError, error_msg): + flexmock_teardown() + mock.should_receive("foo").never # pylint: disable=expression-not-assigned + with assert_raises(exceptions.FlexmockError, error_msg): + flexmock_teardown() + mock.should_receive("foo").mock # pylint: disable=expression-not-assigned + with assert_raises(exceptions.FlexmockError, error_msg): + flexmock_teardown() + + def test_type_flexmock_with_unicode_string_in_should_receive(self): + class Foo: + def method(self): + return "bar" + + flexmock(Foo).should_receive("method").and_return("mocked_bar") + + instance = Foo() + assert instance.method() == "mocked_bar" + + def test_flexmock_should_accept_shortcuts_for_creating_mock_object(self): + mock = flexmock(attr1="value 1", attr2=lambda: "returning 2") + assert mock.attr1 == "value 1" + assert mock.attr2() == "returning 2" + + def test_flexmock_should_accept_shortcuts_for_creating_expectations(self): + class Foo: + def method1(self): + pass + + def method2(self): + pass + + instance = Foo() + flexmock(instance, method1="returning 1", method2="returning 2") + assert instance.method1() == "returning 1" + assert instance.method2() == "returning 2" + assert instance.method2() == "returning 2" + + def test_flexmock_expectations_returns_all(self): + mock = flexmock(name="temp") + assert mock not in FlexmockContainer.flexmock_objects + mock.should_receive("method_foo") + mock.should_receive("method_bar") + assert len(FlexmockContainer.flexmock_objects[mock]) == 2 + + def test_flexmock_expectations_returns_named_expectation(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo") + assert FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._name == "method_foo" + + def test_flexmock_expectations_returns_none_if_not_found(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo") + assert FlexmockContainer.get_flexmock_expectation(mock, "method_bar") is None + + def test_flexmock_should_check_parameters(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").with_args("bar").and_return(1) + mock.should_receive("method_foo").with_args("baz").and_return(2) + assert mock.method_foo("bar") == 1 + assert mock.method_foo("baz") == 2 + + def test_flexmock_should_keep_track_of_calls(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").with_args("foo").and_return(0) + mock.should_receive("method_foo").with_args("bar").and_return(1) + mock.should_receive("method_foo").with_args("baz").and_return(2) + mock.method_foo("bar") + mock.method_foo("bar") + mock.method_foo("baz") + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo", ("foo",)) + assert expectation._times_called == 0 + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo", ("bar",)) + assert expectation._times_called == 2 + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo", ("baz",)) + assert expectation._times_called == 1 + + def test_flexmock_should_set_expectation_call_numbers(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").times(1) + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + with assert_raises( + exceptions.MethodCallError, + "method_foo() expected to be called exactly 1 time, called 0 times", + ): + expectation._verify() + mock.method_foo() + expectation._verify() + + def test_flexmock_should_check_raised_exceptions(self): + mock = flexmock(name="temp") + + class FakeException(Exception): + pass + + mock.should_receive("method_foo").and_raise(FakeException) + with assert_raises(FakeException, ""): + mock.method_foo() + assert FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._times_called == 1 + + def test_flexmock_should_check_raised_exceptions_instance_with_args(self): + mock = flexmock(name="temp") + + class FakeException(Exception): + def __init__(self, arg, arg2): + # pylint: disable=super-init-not-called + pass + + mock.should_receive("method_foo").and_raise(FakeException(1, arg2=2)) + with assert_raises(FakeException, "1"): + mock.method_foo() + assert FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._times_called == 1 + + def test_flexmock_should_check_raised_exceptions_class_with_args(self): + mock = flexmock(name="temp") + + class FakeException(Exception): + def __init__(self, arg, arg2): + # pylint: disable=super-init-not-called + pass + + mock.should_receive("method_foo").and_raise(FakeException, 1, arg2=2) + with assert_raises(FakeException, "1"): + mock.method_foo() + assert FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._times_called == 1 + + def test_flexmock_should_match_any_args_by_default(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("bar") + mock.should_receive("method_foo").with_args("baz").and_return("baz") + assert mock.method_foo() == "bar" + assert mock.method_foo(1) == "bar" + assert mock.method_foo("foo", "bar") == "bar" + assert mock.method_foo("baz") == "baz" + + def test_flexmock_should_fail_to_match_exactly_no_args_when_calling_with_args(self): + mock = flexmock() + mock.should_receive("method_foo").with_args() + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method_foo did not match expectations:\n" + ' Received call:\tmethod_foo("baz")\n' + " Expected call[1]:\tmethod_foo()" + ), + ): + mock.method_foo("baz") + + def test_flexmock_should_match_exactly_no_args(self): + class Foo: + def method(self): + pass + + instance = Foo() + flexmock(instance).should_receive("method").with_args().and_return("baz") + assert instance.method() == "baz" + + def test_expectation_dot_mock_should_return_mock(self): + mock = flexmock(name="temp") + assert mock.should_receive("method_foo").mock() == mock + + def test_flexmock_should_create_partial_new_style_object_mock(self): + class User: + def __init__(self, name=None): + self.name = name + + def get_name(self): + return self.name + + def set_name(self, name): + self.name = name + + user = User() + flexmock(user) + user.should_receive("get_name").and_return("john") + user.set_name("mike") + assert user.get_name() == "john" + + def test_flexmock_should_create_partial_old_style_object_mock(self): + class User: + def __init__(self, name=None): + self.name = name + + def get_name(self): + return self.name + + def set_name(self, name): + self.name = name + + user = User() + flexmock(user) + user.should_receive("get_name").and_return("john") + user.set_name("mike") + assert user.get_name() == "john" + + def test_flexmock_should_create_partial_new_style_class_mock(self): + class User: + def __init__(self): + pass + + def get_name(self): + pass + + flexmock(User) + User.should_receive("get_name").and_return("mike") + user = User() + assert user.get_name() == "mike" + + def test_flexmock_should_create_partial_old_style_class_mock(self): + class User: + def __init__(self): + pass + + def get_name(self): + pass + + flexmock(User) + User.should_receive("get_name").and_return("mike") + user = User() + assert user.get_name() == "mike" + + def test_flexmock_should_match_expectations_against_builtin_classes(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").with_args(str).and_return("got a string") + mock.should_receive("method_foo").with_args(int).and_return("got an int") + assert mock.method_foo("string!") == "got a string" + assert mock.method_foo(23) == "got an int" + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method_foo did not match expectations:\n" + " Received call:\tmethod_foo(2.0)\n" + " Expected call[1]:\tmethod_foo()\n" + " Expected call[2]:\tmethod_foo()" + ), + ): + mock.method_foo(2.0) + + def test_with_args_should_work_with_builtin_c_methods(self): + flexmock(sys.stdout).should_call("write") # set fall-through + flexmock(sys.stdout).should_receive("write").with_args("flexmock_builtin_test") + sys.stdout.write("flexmock_builtin_test") + + def test_with_args_should_work_with_builtin_c_functions(self): + mocked = flexmock(sys) + mocked.should_receive("exit").with_args(1).once() + mocked.exit(1) + flexmock_teardown() + flexmock(os).should_receive("remove").with_args("path").once() + os.remove("path") + + def test_with_args_should_work_with_builtin_python_methods(self): + flexmock(random).should_receive("randint").with_args(1, 10).once() + random.randint(1, 10) + + def test_flexmock_should_match_expectations_against_user_defined_classes(self): + mock = flexmock(name="temp") + + class Foo: + pass + + mock.should_receive("method_foo").with_args(Foo).and_return("got a Foo") + assert mock.method_foo(Foo()) == "got a Foo" + with assert_raises( + exceptions.MethodSignatureError, + ( + "Arguments for call method_foo did not match expectations:\n" + " Received call:\tmethod_foo(1)\n" + " Expected call[1]:\tmethod_foo(.Foo'>)" + ), + ): + mock.method_foo(1) + + def test_flexmock_configures_global_mocks_dict(self): + mock = flexmock(name="temp") + assert mock not in FlexmockContainer.flexmock_objects + mock.should_receive("method_foo") + assert mock in FlexmockContainer.flexmock_objects + assert len(FlexmockContainer.flexmock_objects[mock]) == 1 + + def test_flexmock_teardown_verifies_mocks(self): + mock = flexmock(name="temp") + mock.should_receive("verify_expectations").times(1) + with assert_raises( + exceptions.MethodCallError, + "verify_expectations() expected to be called exactly 1 time, called 0 times", + ): + flexmock_teardown() + + def test_flexmock_teardown_does_not_verify_stubs(self): + mock = flexmock(name="temp") + mock.should_receive("verify_expectations") + flexmock_teardown() + + def test_flexmock_preserves_stubbed_object_methods_between_tests(self): + class User: + def get_name(self): + return "mike" + + user = User() + flexmock(user).should_receive("get_name").and_return("john") + assert user.get_name() == "john" + flexmock_teardown() + assert user.get_name() == "mike" + + def test_flexmock_preserves_stubbed_class_methods_between_tests(self): + class User: + def get_name(self): + return "mike" + + user = User() + flexmock(User).should_receive("get_name").and_return("john") + assert user.get_name() == "john" + flexmock_teardown() + assert user.get_name() == "mike" + + def test_flexmock_removes_new_stubs_from_objects_after_tests(self): + class User: + def get_name(self): + pass + + user = User() + saved = user.get_name + flexmock(user).should_receive("get_name").and_return("john") + assert saved != user.get_name + assert user.get_name() == "john" + flexmock_teardown() + assert user.get_name == saved + + def test_flexmock_removes_new_stubs_from_classes_after_tests(self): + class User: + def get_name(self): + pass + + user = User() + saved = user.get_name + flexmock(User).should_receive("get_name").and_return("john") + assert saved != user.get_name + assert user.get_name() == "john" + flexmock_teardown() + assert user.get_name == saved + + def test_flexmock_removes_stubs_from_multiple_objects_on_teardown(self): + class User: + def get_name(self): + pass + + class Group: + def get_name(self): + pass + + user = User() + group = Group() + saved1 = user.get_name + saved2 = group.get_name + flexmock(user).should_receive("get_name").and_return("john").once() + flexmock(group).should_receive("get_name").and_return("john").once() + assert saved1 != user.get_name + assert saved2 != group.get_name + assert user.get_name() == "john" + assert group.get_name() == "john" + flexmock_teardown() + assert user.get_name == saved1 + assert group.get_name == saved2 + + def test_flexmock_removes_stubs_from_multiple_classes_on_teardown(self): + class User: + def get_name(self): + pass + + class Group: + def get_name(self): + pass + + user = User() + group = User() + saved1 = user.get_name + saved2 = group.get_name + flexmock(User).should_receive("get_name").and_return("john") + flexmock(Group).should_receive("get_name").and_return("john") + assert saved1 != user.get_name + assert saved2 != group.get_name + assert user.get_name() == "john" + assert group.get_name() == "john" + flexmock_teardown() + assert user.get_name == saved1 + assert group.get_name == saved2 + + def test_flexmock_stubs_are_callable(self): + stub = flexmock() + mocked = flexmock() + mocked.should_receive("create").twice() + stub.tickets = mocked + # stub.tickets should work with and without parentheses + stub.tickets().create() + stub.tickets.create() + flexmock_teardown() + + def test_flexmock_respects_at_least_when_called_less_than_requested(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("bar").at_least().twice() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._modifier == AT_LEAST + mock.method_foo() + with assert_raises( + exceptions.MethodCallError, + "method_foo() expected to be called at least 2 times, called 1 time", + ): + flexmock_teardown() + + def test_flexmock_respects_at_least_when_called_requested_number(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar").at_least().once() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._modifier == AT_LEAST + mock.method_foo() + flexmock_teardown() + + def test_flexmock_respects_at_least_when_called_more_than_requested(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar").at_least().once() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._modifier == AT_LEAST + mock.method_foo() + mock.method_foo() + flexmock_teardown() + + def test_flexmock_respects_at_most_when_called_less_than_requested(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("bar").at_most().twice() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._modifier == AT_MOST + mock.method_foo() + flexmock_teardown() + + def test_flexmock_respects_at_most_when_called_requested_number(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar").at_most().once() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._modifier == AT_MOST + mock.method_foo() + flexmock_teardown() + + def test_flexmock_respects_at_most_when_called_more_than_requested(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar").at_most().once() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._modifier == AT_MOST + mock.method_foo() + with assert_raises( + exceptions.MethodCallError, + "method_foo() expected to be called at most 1 time, called 2 times", + ): + mock.method_foo() + + def test_flexmock_treats_once_as_times_one(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar").once() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._expected_calls[EXACTLY] == 1 + with assert_raises( + exceptions.MethodCallError, + "method_foo() expected to be called exactly 1 time, called 0 times", + ): + flexmock_teardown() + + def test_flexmock_treats_twice_as_times_two(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").twice().and_return("value_bar") + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._expected_calls[EXACTLY] == 2 + with assert_raises( + exceptions.MethodCallError, + "method_foo() expected to be called exactly 2 times, called 0 times", + ): + flexmock_teardown() + + def test_flexmock_works_with_never_when_true(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar").never() + expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") + assert expectation._expected_calls[EXACTLY] == 0 + flexmock_teardown() + + def test_flexmock_works_with_never_when_false(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").and_return("value_bar").never() + with assert_raises( + exceptions.MethodCallError, + "method_foo() expected to be called exactly 0 times, called 1 time", + ): + mock.method_foo() + + def test_flexmock_get_flexmock_expectation_should_work_with_args(self): + mock = flexmock(name="temp") + mock.should_receive("method_foo").with_args("value_bar") + assert FlexmockContainer.get_flexmock_expectation(mock, "method_foo", "value_bar") + + def test_mock_kwargs_only_func_mock_all(self): + flexmock(some_module).should_receive("kwargs_only_func1").with_args( + 1, bar=2, baz=3 + ).and_return(123) + assert some_module.kwargs_only_func1(1, bar=2, baz=3) == 123 + + def test_mock_kwargs_only_func_mock_required(self): + flexmock(some_module).should_receive("kwargs_only_func1").with_args(1, bar=2).and_return( + 123 + ) + assert some_module.kwargs_only_func1(1, bar=2) == 123 + + def test_mock_kwargs_only_func_fails_if_required_not_provided(self): + with assert_raises( + exceptions.MethodSignatureError, + 'kwargs_only_func1 requires keyword-only argument "bar"', + ): + flexmock(some_module).should_receive("kwargs_only_func1").with_args(1) + with assert_raises( + exceptions.MethodSignatureError, + 'kwargs_only_func2 requires keyword-only arguments "bar", "baz"', + ): + flexmock(some_module).should_receive("kwargs_only_func2").with_args(2) + + def test_context_manager_on_instance(self): + class CM: + def __enter__(self): + pass + + def __exit__(self, *_): + pass + + instance = CM() + flexmock(instance).should_call("__enter__").once() + flexmock(instance).should_call("__exit__").once() + with instance: + pass + flexmock_teardown() + + def test_context_manager_on_class(self): + class CM: + def __enter__(self): + pass + + def __exit__(self, *_): + pass + + instance = CM() + flexmock(CM).should_receive("__enter__").once() + flexmock(CM).should_receive("__exit__").once() + with instance: + pass + flexmock_teardown() + + def test_flexmock_should_support_with(self): + mocked = flexmock() + with mocked as mock: + mock.should_receive("bar").and_return("baz") + assert mocked.bar() == "baz" + + def test_builtin_open(self): + mock = flexmock(sys.modules["builtins"]) + fake_fd = flexmock(read=lambda: "some data") + mock.should_receive("open").once().with_args("file_name").and_return(fake_fd) + with open("file_name") as file: # pylint: disable=unspecified-encoding + data = file.read() + assert data == "some data" + + def test_mock_multiple_properties(self): + class Foo: + @property + def prop1(self): + return "prop1" + + @property + def prop2(self): + return "prop2" + + mocked = Foo() + flexmock(mocked, prop1="mocked1", prop2="mocked2") + assert mocked.prop1 == "mocked1" + assert mocked.prop2 == "mocked2" + + def test_mock_property_with_attribute_on_instance(self): + class Foo: + @property + def method(self): + return "bar" + + foo1 = Foo() + foo2 = Foo() + foo3 = Foo() + flexmock(foo1, method="baz") + flexmock(foo2, method="baz2") + assert foo1.method == "baz" + assert foo2.method == "baz2" + assert foo3.method == "bar" + flexmock_teardown() + assert hasattr(Foo, "_flexmock__method") is False, "Property method not cleaned up" + assert foo1.method == "bar" + assert foo2.method == "bar" + assert foo3.method == "bar" + + def test_mock_property_with_attribute_on_class(self): + class Foo: + @property + def method(self): + return "bar" + + foo1 = Foo() + foo2 = Foo() + flexmock(Foo, method="baz") + assert foo1.method == "baz" + assert foo2.method == "baz" + flexmock_teardown() + assert hasattr(Foo, "_flexmock__method") is False, "Property method not cleaned up" + assert foo1.method == "bar" + assert foo2.method == "bar" + + def test_verifying_methods_when_mocking_module(self): + # previously, we had problems with recognizing methods vs functions if the mocked + # object was an imported module + flexmock(some_module).should_receive("ModuleClass").with_args(1, 2) + flexmock(some_module).should_receive("module_function").with_args(1, 2) + + def test_fake_object_supporting_iteration(self): + mocked = flexmock() + mocked.should_receive("__iter__").and_yield(1, 2, 3) + assert list(mocked) == [1, 2, 3] + + def test_with_args_for_single_named_arg_with_optional_args(self): + class FooClass: + def method(self, one, two="optional"): + pass + + flexmock(FooClass).should_receive("method").with_args(one=1) + + def test_with_args_doesnt_set_max_when_using_varargs(self): + class FooClass: + def method(self, *args): + pass + + flexmock(FooClass).should_receive("method").with_args(1, 2, 3) + + def test_with_args_doesnt_set_max_when_using_kwargs(self): + class FooClass: + def method(self, **kwargs): + pass + + flexmock(FooClass).should_receive("method").with_args(1, 2, 3) + + def test_with_args_blows_up_on_too_few_args(self): + class FooClass: + def method1(self, arg1): + pass + + def method2(self, arg1, arg2, arg3=1): + pass + + mock = flexmock(FooClass) + expectation1 = mock.should_receive("method1") + expectation2 = mock.should_receive("method2") + with assert_raises( + exceptions.MethodSignatureError, + "method1 requires at least 1 argument, expectation provided 0", + ): + expectation1.with_args() + with assert_raises( + exceptions.MethodSignatureError, + "method2 requires at least 2 arguments, expectation provided 1", + ): + expectation2.with_args(1) + + def test_with_args_blows_up_on_too_few_args_with_kwargs(self): + class FooClass: + def method(self, arg1, arg2, arg3=1): + pass + + expectation = flexmock(FooClass).should_receive("method") + with assert_raises( + exceptions.MethodSignatureError, + "method requires at least 3 arguments, expectation provided 2", + ): + expectation.with_args(1, arg3=2) + + def test_with_args_blows_up_on_too_many_args(self): + class FooClass: + def method(self, arg1, arg2, arg3=1): + pass + + expectation = flexmock(FooClass).should_receive("method") + with assert_raises( + exceptions.MethodSignatureError, + "method requires at most 3 arguments, expectation provided 4", + ): + expectation.with_args(1, 2, 3, 4) + + def test_with_args_blows_up_on_kwarg_overlapping_positional(self): + class FooClass: + def method(self, arg1, arg2, arg3=1, **kwargs): + pass + + expectation = flexmock(FooClass).should_receive("method") + with assert_raises( + exceptions.MethodSignatureError, + "['arg3'] already given as positional argument to method", + ): + expectation.with_args(1, 2, 3, arg3=2) + with assert_raises( + exceptions.MethodSignatureError, + "['arg3', 'arg2'] already given as positional arguments to method", + ): + expectation.with_args(1, 2, 3, arg3=2, arg2=3) + + def test_with_args_blows_up_on_invalid_kwarg(self): + class FooClass: + def method(self, arg1, arg2, arg3=1): + pass + + with assert_raises( + exceptions.MethodSignatureError, "d is not a valid keyword argument to method" + ): + flexmock(FooClass).should_receive("method").with_args(1, 2, d=2) + + def test_with_args_ignores_invalid_args_on_flexmock_instances(self): + instance = flexmock(bar=lambda x: x) + instance.should_receive("bar").with_args("stuff") + instance.bar("stuff") + + def test_with_args_does_not_compensate_for_self_on_static_instance_methods(self): + class FooClass: + @staticmethod + def method(arg): + pass + + instance = FooClass() + flexmock(instance).should_receive("method").with_args("stuff") + instance.method("stuff") + + def test_with_args_does_not_compensate_for_self_on_static_class_methods(self): + class FooClass: + @staticmethod + def method(arg): + pass + + flexmock(FooClass).should_receive("method").with_args("stuff") + FooClass.method("stuff") + + def test_with_args_does_compensate_for_cls_on_class_methods(self): + class FooClass: + @classmethod + def method(cls, arg): + pass + + instance = FooClass() + flexmock(instance).should_receive("method").with_args("stuff") + instance.method("stuff") + + def test_calling_with_keyword_args_matches_mock_with_positional_args(self): + class FooClass: + def method(self, arg1, arg2, arg3): + pass + + instance = FooClass() + flexmock(instance).should_receive("method").with_args(1, 2, 3).once() + instance.method(arg1=1, arg2=2, arg3=3) + + def test_calling_with_positional_args_matches_mock_with_kwargs(self): + class FooClass: + def method(self, arg1, arg2, arg3): + pass + + instance = FooClass() + flexmock(instance).should_receive("method").with_args(arg1=1, arg2=2, arg3=3).once() + instance.method(1, 2, arg3=3) + + def test_and_return_defaults_to_none_with_no_arguments(self): + mocked = flexmock() + mocked.should_receive("bar").and_return() + assert mocked.bar() is None + + def test_non_callable_attributes_fail_to_set_expectations(self): + class FooClass: + attribute = 1 + + instance = FooClass() + expectation = flexmock(instance).should_receive("attribute").and_return(2) + with assert_raises(exceptions.FlexmockError, "can't use times() with attribute stubs"): + expectation.times(1) + with assert_raises(exceptions.FlexmockError, "can't use with_args() with attribute stubs"): + expectation.with_args(()) + with assert_raises( + exceptions.FlexmockError, "can't use replace_with() with attribute/property stubs" + ): + expectation.replace_with(lambda x: x) + with assert_raises(exceptions.FlexmockError, "can't use and_raise() with attribute stubs"): + expectation.and_raise(Exception) + with assert_raises(exceptions.FlexmockError, "can't use when() with attribute stubs"): + expectation.when(lambda x: x) + with assert_raises(exceptions.FlexmockError, "can't use and_yield() with attribute stubs"): + expectation.and_yield(1) + with assert_raises(exceptions.FlexmockError, "can't use ordered() with attribute stubs"): + object.__getattribute__(expectation, "ordered")() + with assert_raises(exceptions.FlexmockError, "can't use at_least() with attribute stubs"): + object.__getattribute__(expectation, "at_least")() + with assert_raises(exceptions.FlexmockError, "can't use at_most() with attribute stubs"): + object.__getattribute__(expectation, "at_most")() + with assert_raises(exceptions.FlexmockError, "can't use one_by_one() with attribute stubs"): + object.__getattribute__(expectation, "one_by_one")() + + def test_should_chain_attributes(self): + class Class1: + x = 1 + + class Class2: + class1 = Class1() + + class Class3: + class2 = Class2() + + class3 = Class3() + mocked = flexmock(class3) + mocked.should_receive("class2.class1.x").and_return(2) + assert mocked.class2.class1.x == 2 + flexmock_teardown() + assert mocked.class2.class1.x == 1 + + def test_mocking_subclass_of_str(self): + class String(str): + pass + + string = String() + flexmock(string, endswith="fake") + assert string.endswith("stuff") == "fake" + flexmock_teardown() + assert string.endswith("stuff") is False + + def test_proper_reset_of_subclass_methods(self): + class ClassA: + def method(self): + return "a" + + class ClassB(ClassA): + def method(self): + return "b" + + flexmock(ClassB).should_receive("method").and_return("1") + flexmock_teardown() + assert ClassB().method() == "b" + + def test_flexmock_class_returns_same_object_on_repeated_calls(self): + class Foo: + pass + + first = flexmock(Foo) + second = flexmock(Foo) + assert first is second + + def test_flexmock_object_returns_same_object_on_repeated_calls(self): + class Foo: + pass + + instance = Foo() + first = flexmock(instance) + second = flexmock(instance) + assert first is second + + def test_mocking_down_the_inheritance_chain_class_to_class(self): + class Parent: + def method1(self): + pass + + class Child(Parent): + def method2(self): + pass + + flexmock(Parent).should_receive("method1").and_return("outer") + flexmock(Child).should_receive("method2").and_return("inner") + assert Parent().method1() == "outer" + assert Child().method2() == "inner" + + def test_flexmock_should_mock_the_same_method_multiple_times(self): + class Foo: + def method(self): + pass + + instance = Foo() + flexmock(instance).should_receive("method").and_return(1) + assert instance.method() == 1 + flexmock(instance).should_receive("method").and_return(2) + assert instance.method() == 2 + flexmock(instance).should_receive("method").and_return(3) + assert instance.method() == 3 + flexmock(instance).should_receive("method").and_return(4) + assert instance.method() == 4 + + def test_new_instances_should_be_a_method(self): + class Foo: + pass + + flexmock(Foo).new_instances("bar") + assert Foo() == "bar" + flexmock_teardown() + assert Foo() != "bar" + + def test_new_instances_raises_error_when_not_a_class(self): + class Foo: + pass + + instance = Foo() + flexmock(instance) + with assert_raises( + exceptions.FlexmockError, "new_instances can only be called on a class mock" + ): + instance.new_instances("bar") + + def test_new_instances_works_with_multiple_return_values(self): + class Foo: + pass + + flexmock(Foo).new_instances("foo", "bar") + assert Foo() == "foo" + assert Foo() == "bar" + + def test_should_receive_should_not_replace_flexmock_methods(self): + class Foo: + pass + + instance = Foo() + flexmock(instance) + for method_name in ["should_receive", "should_call", "new_instances"]: + with assert_raises(exceptions.FlexmockError, "unable to replace flexmock methods"): + instance.should_receive(method_name) + + def test_flexmock_should_not_add_methods_if_they_already_exist(self): + class Foo: + def should_receive(self): + return "real" + + def method(self): + pass + + instance = Foo() + mock = flexmock(instance) + assert instance.should_receive() == "real" + assert "should_call" not in dir(instance) + assert "new_instances" not in dir(instance) + mock.should_receive("method").and_return("baz") + assert instance.method() == "baz" + flexmock_teardown() + assert instance.should_receive() == "real" + + def test_flexmock_should_not_add_class_methods_if_they_already_exist(self): + class Foo: + def should_receive(self): + return "real" + + def method(self): + pass + + instance = Foo() + mock = flexmock(Foo) + assert instance.should_receive() == "real" + assert "should_call" not in dir(Foo) + assert "new_instances" not in dir(Foo) + mock.should_receive("method").and_return("baz") + assert instance.method() == "baz" + flexmock_teardown() + assert instance.should_receive() == "real" + + def test_flexmock_should_replace_method(self): + class Foo: + def method(self, arg): + return arg + + instance = Foo() + flexmock(instance).should_receive("method").replace_with(lambda x: x == 5) + assert instance.method(5) is True + assert instance.method(4) is False + + def test_flexmock_should_replace_cannot_be_specified_twice(self): + class Foo: + def method(self, arg): + return arg + + instance = Foo() + expectation = flexmock(instance).should_receive("method").replace_with(lambda x: x == 5) + with assert_raises(exceptions.FlexmockError, "replace_with cannot be specified twice"): + expectation.replace_with(lambda x: x == 3) + + def test_should_give_reasonable_error_for_builtins(self): + with assert_raises( + exceptions.MockBuiltinError, + ( + "Python does not allow you to mock builtin objects or modules. " + "Consider wrapping it in a class you can mock instead" + ), + ): + flexmock(object) + + def test_should_give_reasonable_error_for_instances_of_builtins(self): + with assert_raises( + exceptions.MockBuiltinError, + ( + "Python does not allow you to mock instances of builtin objects. " + "Consider wrapping it in a class you can mock instead" + ), + ): + flexmock(object()) + + def test_mock_chained_method_calls_works_with_one_level(self): + class Foo: + def method2(self): + return "foo" + + class Bar: + def method1(self): + return Foo() + + instance = Bar() + assert instance.method1().method2() == "foo" + flexmock(instance).should_receive("method1.method2").and_return("bar") + assert instance.method1().method2() == "bar" + + def test_mock_chained_method_supports_args_and_mocks(self): + class Foo: + def method2(self, arg): + return arg + + class Bar: + def method1(self): + return Foo() + + instance = Bar() + assert instance.method1().method2("foo") == "foo" + flexmock(instance).should_receive("method1.method2").with_args("foo").and_return( + "bar" + ).once() + assert instance.method1().method2("foo") == "bar" + flexmock_teardown() + flexmock(instance).should_receive("method1.method2").with_args("foo").and_return( + "bar" + ).once() + with assert_raises( + exceptions.MethodCallError, + 'method2("foo") expected to be called exactly 1 time, called 0 times', + ): + flexmock_teardown() + + def test_mock_chained_method_calls_works_with_more_than_one_level(self): + class Baz: + def method3(self): + return "foo" + + class Foo: + def method2(self): + return Baz() + + class Bar: + def method1(self): + return Foo() + + instance = Bar() + assert instance.method1().method2().method3() == "foo" + flexmock(instance).should_receive("method1.method2.method3").and_return("bar") + assert instance.method1().method2().method3() == "bar" + + def test_flexmock_should_fail_mocking_nonexistent_methods(self): + class User: + pass + + user = User() + with assert_raises( + exceptions.FlexmockError, + re.compile( + r"<.+\.\.User object at 0x.+> does not have attribute 'nonexistent'" + ), + ): + flexmock(user).should_receive("nonexistent") + + def test_new_instances_should_blow_up_on_should_receive(self): + class User: + pass + + mock = flexmock(User).new_instances(None).mock() + with assert_raises(exceptions.FlexmockError, "User does not have attribute 'foo'"): + mock.should_receive("foo") + + def test_flexmock_should_support_mocking_classes_as_functions(self): + flexmock(some_module).should_receive("SomeClass").and_return("yay") + assert some_module.SomeClass() == "yay" + + def test_flexmock_should_properly_restore_class_methods(self): + class User: + @classmethod + def get_stuff(cls): + return cls.__name__ + + assert isinstance(User.__dict__["get_stuff"], classmethod) + assert User.get_stuff() == "User" + flexmock(User).should_receive("get_stuff").and_return("foo") + assert User.get_stuff() == "foo" + flexmock_teardown() + assert User.get_stuff() == "User" + assert isinstance(User.__dict__["get_stuff"], classmethod) + + def test_flexmock_should_mock_same_class_twice(self): + class Foo: + pass + + flexmock(Foo) + flexmock(Foo) + + def test_and_raise_with_invalid_arguments(self): + with assert_raises( + exceptions.FlexmockError, + "can't initialize with the given arguments", + ): + flexmock().should_receive("foo").and_raise(Exception, 1, bar=2) + + def test_flexmock_should_mock_iter_on_new_style_instances(self): + class Foo: + def __iter__(self): + yield None + + old = Foo.__iter__ + foo1 = Foo() + foo2 = Foo() + foo3 = Foo() + flexmock(foo1, __iter__=iter([1, 2, 3])) + flexmock(foo2, __iter__=iter([3, 4, 5])) + assert list(foo1) == [1, 2, 3] + assert list(foo2) == [3, 4, 5] + assert list(foo3) == [None] + assert foo1.__iter__ is not old + assert foo2.__iter__ is not old + assert foo3.__iter__ is not old + flexmock_teardown() + assert list(foo1) == [None] + assert list(foo2) == [None] + assert list(foo3) == [None] + assert Foo.__iter__ is old, f"{Foo.__iter__} != {old}" + + def test_flexmock_should_mock_private_methods_with_leading_underscores(self): + class ClassWithPrivateMethods: + def __private_instance_method(self): + pass + + @classmethod + def __private_class_method(cls): + pass + + def instance_method(self): + return self.__private_instance_method() + + @classmethod + def class_method(cls): + return cls.__private_class_method() + + # Instance + instance = ClassWithPrivateMethods() + flexmock(instance).should_receive("__private_instance_method").and_return("bar") + assert instance.instance_method() == "bar" + + # Class + flexmock(ClassWithPrivateMethods).should_receive("__private_class_method").and_return("baz") + assert ClassWithPrivateMethods.class_method() == "baz" + + def test_flexmock_should_mock_generators(self): + class Gen: + def method(self): + pass + + gen = Gen() + flexmock(gen).should_receive("method").and_yield(*range(1, 10)) + output = list(gen.method()) + assert list(range(1, 10)) == output + + def test_flexmock_should_mock_private_methods(self): + class Foo: + def __private_method(self): + return "foo" + + def public_method(self): + return self.__private_method() + + instance = Foo() + flexmock(instance).should_receive("__private_method").and_return("bar") + assert instance.public_method() == "bar" + + def test_flexmock_should_mock_special_methods(self): + class Foo: + def __special_method__(self): + return "foo" + + def public_method(self): + return self.__special_method__() + + instance = Foo() + flexmock(instance).should_receive("__special_method__").and_return("bar") + assert instance.public_method() == "bar" + + def test_flexmock_should_mock_double_underscore_method(self): + class Foo: + def __(self): + return "foo" + + def public_method(self): + return self.__() + + instance = Foo() + flexmock(instance).should_receive("__").and_return("bar") + assert instance.public_method() == "bar" + + def test_flexmock_should_mock_private_class_methods(self): + class Foo: + def __iter__(self): + pass + + flexmock(Foo).should_receive("__iter__").and_yield(1, 2, 3) + assert list(Foo()) == [1, 2, 3] + + def test_flexmock_doesnt_error_on_properly_ordered_expectations(self): + class Foo: + def method1(self): + pass + + def method2(self, arg1): + pass + + def method3(self): + pass + + def method4(self): + pass + + instance = Foo() + flexmock(instance).should_receive("method1") + flexmock(instance).should_receive("method2").with_args("a").ordered() + flexmock(instance).should_receive("method3") + flexmock(instance).should_receive("method2").with_args("b").ordered() + flexmock(instance).should_receive("method4") + instance.method3() + instance.method2("a") + instance.method2("b") + instance.method4() + instance.method1() + + def test_flexmock_errors_on_improperly_ordered_expectations(self): + class Foo: + def method1(self, arg1): + pass + + instance = Foo() + flexmock(instance) + instance.should_receive("method1").with_args("a").ordered() + instance.should_receive("method1").with_args("b").ordered() + with assert_raises( + exceptions.CallOrderError, 'method1("b") called before method1(arg1="a")' + ): + instance.method1("b") + + def test_flexmock_should_accept_multiple_return_values(self): + class Foo: + def method1(self): + pass + + instance = Foo() + flexmock(instance).should_receive("method1").and_return(1, 5).and_return(2) + assert instance.method1() == (1, 5) + assert instance.method1() == 2 + assert instance.method1() == (1, 5) + assert instance.method1() == 2 + + def test_flexmock_should_accept_multiple_return_values_with_shortcut(self): + class Foo: + def method1(self): + pass + + instance = Foo() + flexmock(instance).should_receive("method1").and_return(1, 2).one_by_one() + assert instance.method1() == 1 + assert instance.method1() == 2 + assert instance.method1() == 1 + assert instance.method1() == 2 + + def test_flexmock_should_accept_multiple_return_values_with_one_by_one(self): + mocked = flexmock() + flexmock(mocked).should_receive("method1").and_return(2).and_return(3).one_by_one() + assert mocked.method1() == 2 + assert mocked.method1() == 3 + assert mocked.method1() == 2 + assert mocked.method1() == 3 + + def test_one_by_one_called_before_and_return_multiple_values(self): + mocked = flexmock() + mocked.should_receive("method1").one_by_one().and_return(3, 4) + assert mocked.method1() == 3 + assert mocked.method1() == 4 + assert mocked.method1() == 3 + assert mocked.method1() == 4 + + def test_one_by_one_called_before_and_return_one_value(self): + mocked = flexmock() + mocked.should_receive("method1").one_by_one().and_return(4).and_return(5) + assert mocked.method1() == 4 + assert mocked.method1() == 5 + assert mocked.method1() == 4 + assert mocked.method1() == 5 + + def test_flexmock_should_mix_multiple_return_values_with_exceptions(self): + class Foo: + def method1(self): + pass + + instance = Foo() + flexmock(instance).should_receive("method1").and_return(1).and_raise(Exception) + assert instance.method1() == 1 + with assert_raises(Exception, ""): + instance.method1() + assert instance.method1() == 1 + with assert_raises(Exception, ""): + instance.method1() + + def test_flexmock_should_mock_new_instances_with_multiple_params(self): + class User: + pass + + class Group: + def __init__(self, arg, arg2): + pass + + user = User() + flexmock(Group).new_instances(user) + assert user is Group(1, 2) + + def test_flexmock_function_should_return_previously_mocked_object(self): + class User: + pass + + user = User() + instance = flexmock(user) + assert instance == user + assert instance == flexmock(user) + + def test_flexmock_should_not_return_class_object_if_mocking_instance(self): + class User: + def method(self): + pass + + user = User() + user2 = User() + class_mock = flexmock(User).should_receive("method").and_return("class").mock() + user_mock = flexmock(user).should_receive("method").and_return("instance").mock() + assert class_mock is not user_mock + assert user.method() == "instance" + assert user2.method() == "class" diff --git a/tests/features/ordered.py b/tests/features/ordered.py new file mode 100644 index 0000000..c31fc06 --- /dev/null +++ b/tests/features/ordered.py @@ -0,0 +1,50 @@ +"""Tests for flexmock ordered feature.""" +# pylint: disable=missing-docstring,no-self-use,no-member +import re + +from flexmock import exceptions, flexmock +from tests.utils import assert_raises + + +class OrderedTestCase: + def test_ordered_on_different_methods(self): + class String(str): + pass + + string = String("abc") + flexmock(string) + string.should_call("startswith").with_args("asdf", 0, 4).ordered() + string.should_call("endswith").ordered() + with assert_raises( + exceptions.CallOrderError, + re.compile( + r'endswith\("c"\) called before startswith' + # Argument names are displayed in PyPy + r'\((prefix=)?"asdf", (start=)?0, (end=)?4\)' + ), + ): + string.endswith("c") + + def test_flexmock_ordered_worked_after_default_stub(self): + mocked = flexmock() + mocked.should_receive("bar") + mocked.should_receive("bar").with_args("a").ordered() + mocked.should_receive("bar").with_args("b").ordered() + with assert_raises(exceptions.CallOrderError, 'bar("b") called before bar("a")'): + mocked.bar("b") + + def test_flexmock_ordered_works_with_same_args(self): + mocked = flexmock() + mocked.should_receive("bar").ordered().and_return(1) + mocked.should_receive("bar").ordered().and_return(2) + assert mocked.bar() == 1 + assert mocked.bar() == 2 + + def test_flexmock_ordered_works_with_same_args_after_default_stub(self): + mocked = flexmock() + mocked.should_receive("bar").and_return(9) + mocked.should_receive("bar").ordered().and_return(1) + mocked.should_receive("bar").ordered().and_return(2) + assert mocked.bar() == 1 + assert mocked.bar() == 2 + assert mocked.bar() == 9 diff --git a/tests/features/proxied.py b/tests/features/proxied.py new file mode 100644 index 0000000..32ac6f9 --- /dev/null +++ b/tests/features/proxied.py @@ -0,0 +1,148 @@ +"""Tests for mocking and spying proxied objects.""" +# pylint: disable=missing-docstring,no-self-use,no-member +from flexmock import flexmock +from tests import some_module +from tests.proxy import Proxy +from tests.some_module import DerivedClass, SomeClass + + +class ProxiedTestCase: + def test_mock_proxied_derived_class_with_args(self): + # pylint: disable=not-callable,invalid-name + DerivedClassProxy = Proxy(DerivedClass) + flexmock(DerivedClassProxy).should_receive("class_method_with_args").with_args( + "a" + ).and_return(2).twice() + assert DerivedClassProxy().class_method_with_args("a") == 2 + assert DerivedClassProxy.class_method_with_args("a") == 2 + flexmock(DerivedClassProxy).should_receive("static_method_with_args").with_args( + "b" + ).and_return(3).twice() + assert DerivedClassProxy().static_method_with_args("b") == 3 + assert DerivedClassProxy.static_method_with_args("b") == 3 + instance = DerivedClassProxy() + flexmock(instance).should_receive("instance_method_with_args").with_args("c").and_return( + 4 + ).once() + assert instance.instance_method_with_args("c") == 4 + + def test_mock_proxied_module_function(self): + # pylint: disable=not-callable + some_module_proxy = Proxy(some_module) + flexmock(some_module_proxy).should_receive("module_function").and_return(3).once() + assert some_module_proxy.module_function() == 3 + + def test_mock_proxied_derived_class(self): + # pylint: disable=not-callable,invalid-name + DerivedClassProxy = Proxy(DerivedClass) + flexmock(DerivedClassProxy).should_receive("class_method").and_return(2).twice() + assert DerivedClassProxy().class_method() == 2 + assert DerivedClassProxy.class_method() == 2 + flexmock(DerivedClassProxy).should_receive("static_method").and_return(3).twice() + assert DerivedClassProxy().static_method() == 3 + assert DerivedClassProxy.static_method() == 3 + instance = DerivedClassProxy() + flexmock(instance).should_receive("instance_method").and_return(4).once() + assert instance.instance_method() == 4 + + def test_mock_proxied_class_with_args(self): + # pylint: disable=not-callable,invalid-name + SomeClassProxy = Proxy(SomeClass) + flexmock(SomeClassProxy).should_receive("class_method_with_args").with_args("a").and_return( + 2 + ).twice() + assert SomeClassProxy().class_method_with_args("a") == 2 + assert SomeClassProxy.class_method_with_args("a") == 2 + flexmock(SomeClassProxy).should_receive("static_method_with_args").with_args( + "b" + ).and_return(3).twice() + assert SomeClassProxy().static_method_with_args("b") == 3 + assert SomeClassProxy.static_method_with_args("b") == 3 + instance = SomeClassProxy() + flexmock(instance).should_receive("instance_method_with_args").with_args("c").and_return( + 4 + ).once() + assert instance.instance_method_with_args("c") == 4 + + def test_mock_proxied_class(self): + # pylint: disable=not-callable,invalid-name + SomeClassProxy = Proxy(SomeClass) + flexmock(SomeClassProxy).should_receive("class_method").and_return(2).twice() + assert SomeClassProxy().class_method() == 2 + assert SomeClassProxy.class_method() == 2 + flexmock(SomeClassProxy).should_receive("static_method").and_return(3).twice() + assert SomeClassProxy().static_method() == 3 + assert SomeClassProxy.static_method() == 3 + instance = SomeClassProxy() + flexmock(instance).should_receive("instance_method").and_return(4).once() + assert instance.instance_method() == 4 + + def test_spy_proxied_derived_class(self): + # pylint: disable=not-callable,invalid-name + DerivedClassProxy = Proxy(DerivedClass) + flexmock(DerivedClassProxy).should_call("class_method").and_return("class_method").twice() + assert DerivedClassProxy().class_method() == "class_method" + assert DerivedClassProxy.class_method() == "class_method" + flexmock(DerivedClassProxy).should_call("static_method").and_return("static_method").twice() + assert DerivedClassProxy().static_method() == "static_method" + assert DerivedClassProxy.static_method() == "static_method" + instance = DerivedClassProxy() + flexmock(instance).should_call("instance_method").and_return("instance_method").once() + assert instance.instance_method() == "instance_method" + + def test_spy_proxied_derived_class_with_args(self): + # pylint: disable=not-callable,invalid-name + DerivedClassProxy = Proxy(DerivedClass) + flexmock(DerivedClassProxy).should_call("class_method_with_args").with_args("a").and_return( + "a" + ).twice() + assert DerivedClassProxy().class_method_with_args("a") == "a" + assert DerivedClassProxy.class_method_with_args("a") == "a" + flexmock(DerivedClassProxy).should_call("static_method_with_args").with_args( + "b" + ).and_return("b").twice() + assert DerivedClassProxy().static_method_with_args("b") == "b" + assert DerivedClassProxy.static_method_with_args("b") == "b" + instance = DerivedClassProxy() + flexmock(instance).should_call("instance_method_with_args").with_args("c").and_return( + "c" + ).once() + assert instance.instance_method_with_args("c") == "c" + + def test_spy_proxied_module_function(self): + # pylint: disable=not-callable + some_module_proxy = Proxy(some_module) + flexmock(some_module_proxy).should_receive("module_function").and_return(0).once() + assert some_module_proxy.module_function(2, 2) == 0 + + def test_spy_proxied_class(self): + # pylint: disable=not-callable,invalid-name + SomeClassProxy = Proxy(SomeClass) + flexmock(SomeClassProxy).should_call("class_method").and_return("class_method").twice() + assert SomeClassProxy().class_method() == "class_method" + assert SomeClassProxy.class_method() == "class_method" + flexmock(SomeClassProxy).should_call("static_method").and_return("static_method").twice() + assert SomeClassProxy().static_method() == "static_method" + assert SomeClassProxy.static_method() == "static_method" + instance = SomeClassProxy() + flexmock(instance).should_call("instance_method").and_return("instance_method").once() + assert instance.instance_method() == "instance_method" + + def test_spy_proxied_class_with_args(self): + # pylint: disable=not-callable,invalid-name + SomeClassProxy = Proxy(SomeClass) + flexmock(SomeClassProxy).should_call("class_method_with_args").with_args("a").and_return( + "a" + ).twice() + assert SomeClassProxy().class_method_with_args("a") == "a" + assert SomeClassProxy.class_method_with_args("a") == "a" + flexmock(SomeClassProxy).should_call("static_method_with_args").with_args("b").and_return( + "b" + ).twice() + assert SomeClassProxy().static_method_with_args("b") == "b" + assert SomeClassProxy.static_method_with_args("b") == "b" + instance = SomeClassProxy() + flexmock(instance).should_call("instance_method_with_args").with_args("c").and_return( + "c" + ).once() + assert instance.instance_method_with_args("c") == "c" diff --git a/tests/features/spying.py b/tests/features/spying.py new file mode 100644 index 0000000..1bea7aa --- /dev/null +++ b/tests/features/spying.py @@ -0,0 +1,502 @@ +"""Tests for flexmock spying feature.""" +# pylint: disable=missing-docstring,no-self-use,no-member +import re + +from flexmock import exceptions, flexmock +from flexmock._api import flexmock_teardown +from tests.utils import assert_raises + + +class SpyingTestCase: + def test_spying_non_existent_mock_object_method_should_fail(self): + mock = flexmock() + with assert_raises( + exceptions.FlexmockError, + "Mock object does not have attribute 'method_foo'. " + 'Did you mean to call should_receive("method_foo") instead?', + ): + mock.should_call("method_foo") + mock = flexmock(method_foo=lambda: "ok") + mock.should_call("method_foo") + + def test_pass_thru_should_call_original_method_only_once(self): + class Nyan: + def __init__(self): + self.value = 0 + + def method(self): + self.value += 1 + + obj = Nyan() + flexmock(obj) + obj.should_call("method") + obj.method() + assert obj.value == 1 + + def test_should_call_works_for_same_method_with_different_args(self): + class Foo: + def method(self, arg): + pass + + instance = Foo() + flexmock(instance).should_call("method").with_args("foo").once() + flexmock(instance).should_call("method").with_args("bar").once() + instance.method("foo") + instance.method("bar") + flexmock_teardown() + + def test_should_call_fails_properly_for_same_method_with_different_args(self): + class Foo: + def method(self, arg): + pass + + instance = Foo() + flexmock(instance).should_call("method").with_args("foo").once() + flexmock(instance).should_call("method").with_args("bar").once() + instance.method("foo") + with assert_raises( + exceptions.MethodCallError, + 'method(arg="bar") expected to be called exactly 1 time, called 0 times', + ): + flexmock_teardown() + + def test_should_call_alias_should_create_a_spy(self): + class Foo: + def get_stuff(self): + return "yay" + + instance = Foo() + flexmock(instance).should_call("get_stuff").and_return("yay").once() + with assert_raises( + exceptions.MethodCallError, + "get_stuff() expected to be called exactly 1 time, called 0 times", + ): + flexmock_teardown() + + def test_spy_should_not_match_falsy_stuff(self): + class Foo: + def method1(self): + return None + + def method2(self): + return False + + def method3(self): + return [] + + def method4(self): + return "" + + instance = Foo() + flexmock(instance).should_call("method1").and_return("bar").once() + flexmock(instance).should_call("method2").and_return("bar").once() + flexmock(instance).should_call("method3").and_return("bar").once() + flexmock(instance).should_call("method4").and_return("bar").once() + with assert_raises( + exceptions.FlexmockError, + ( + "Returned values for call method1 did not match expectation:\n" + " Expected:\t'bar'\n" + " Returned:\tNone" + ), + ): + instance.method1() + with assert_raises( + exceptions.FlexmockError, + ( + "Returned values for call method2 did not match expectation:\n" + " Expected:\t'bar'\n" + " Returned:\tFalse" + ), + ): + instance.method2() + with assert_raises( + exceptions.FlexmockError, + ( + "Returned values for call method3 did not match expectation:\n" + " Expected:\t'bar'\n" + " Returned:\t[]" + ), + ): + instance.method3() + with assert_raises( + exceptions.FlexmockError, + ( + "Returned values for call method4 did not match expectation:\n" + " Expected:\t'bar'\n" + " Returned:\t''" + ), + ): + instance.method4() + + def test_spy_should_match_return_value_class(self): + class User: + pass + + user = User() + instance = flexmock( + foo=lambda: ("bar", "baz"), + bar=lambda: user, + baz=lambda: None, + bax=lambda: None, + ) + instance.should_call("foo").and_return(str, str) + instance.should_call("bar").and_return(User) + instance.should_call("baz").and_return(object) + instance.should_call("bax").and_return(None) + assert instance.foo() == ("bar", "baz") + assert instance.bar() == user + assert instance.baz() is None + assert instance.bax() is None + + def test_flexmock_spy_should_not_clobber_original_method(self): + class User: + def get_stuff(self): + return "real", "stuff" + + user = User() + flexmock(user).should_call("get_stuff") + flexmock(user).should_call("get_stuff") + assert user.get_stuff() == ("real", "stuff") + + def test_and_raise_with_value_that_is_not_a_class(self): + class RaisesException: + def get_stuff(self): + raise RuntimeError("baz") + + instance = RaisesException() + flexmock(instance).should_call("get_stuff").and_raise(RuntimeError("foo")).once() + with assert_raises( + exceptions.ExceptionClassError, + re.compile( + r"Raised exception for call get_stuff did not match expectation:\n" + # Python 3.6 contains comma after 'foo' + r" Expected:\tRuntimeError\('foo',?\)\n" + r" Raised:\t\n\n" + r"Did you try to call and_raise with an instance\?\n" + r'Instead of and_raise\(Exception\("arg"\)\), try and_raise\(Exception, "arg"\)', + ), + ): + instance.get_stuff() + + def test_flexmock_should_blow_up_on_wrong_spy_return_values(self): + class User: + def get_stuff(self): + return "real", "stuff" + + def get_more_stuff(self): + return "other", "stuff" + + user = User() + flexmock(user).should_call("get_stuff").and_return("other", "stuff") + with assert_raises( + exceptions.MethodSignatureError, + ( + "Returned values for call get_stuff did not match expectation:\n" + " Expected:\t('other', 'stuff')\n" + " Returned:\t('real', 'stuff')" + ), + ): + user.get_stuff() + flexmock(user).should_call("get_more_stuff").and_return() + with assert_raises( + exceptions.MethodSignatureError, + ( + "Returned values for call get_more_stuff did not match expectation:\n" + " Expected:\tNone\n" + " Returned:\t('other', 'stuff')" + ), + ): + user.get_more_stuff() + + def test_flexmock_should_reraise_exception_in_spy_with_return_values(self): + class RaisesException: + @classmethod + def class_method(cls): + raise RuntimeError("foo") + + @staticmethod + def static_method(): + raise RuntimeError("bar") + + def instance_method(self): + raise RuntimeError("baz") + + instance = RaisesException() + flexmock(instance).should_call("class_method").and_return("foo").once() + flexmock(instance).should_call("static_method").and_return("bar").once() + flexmock(instance).should_call("instance_method").and_return("baz").once() + + with assert_raises(RuntimeError, "foo"): + instance.class_method() + with assert_raises(RuntimeError, "bar"): + instance.static_method() + with assert_raises(RuntimeError, "baz"): + instance.instance_method() + + flexmock(RaisesException).should_call("class_method").once() + flexmock(RaisesException).should_call("static_method").once() + with assert_raises(RuntimeError, "foo"): + RaisesException.class_method() + with assert_raises(RuntimeError, "bar"): + RaisesException.static_method() + + def test_flexmock_should_reraise_exception_in_spy(self): + class RaisesException: + @classmethod + def class_method(cls): + raise RuntimeError("foo") + + @staticmethod + def static_method(): + raise RuntimeError("bar") + + def instance_method(self): + raise RuntimeError("baz") + + instance = RaisesException() + flexmock(instance).should_call("class_method").once() + flexmock(instance).should_call("static_method").once() + flexmock(instance).should_call("instance_method").once() + + with assert_raises(RuntimeError, "foo"): + instance.class_method() + with assert_raises(RuntimeError, "bar"): + instance.static_method() + with assert_raises(RuntimeError, "baz"): + instance.instance_method() + + flexmock(RaisesException).should_call("class_method").once() + flexmock(RaisesException).should_call("static_method").once() + with assert_raises(RuntimeError, "foo"): + RaisesException.class_method() + with assert_raises(RuntimeError, "bar"): + RaisesException.static_method() + + def test_flexmock_should_verify_correct_spy_return_values(self): + class User: + def get_stuff(self): + return "real", "stuff" + + user = User() + flexmock(user).should_call("get_stuff").and_return("real", "stuff") + assert user.get_stuff() == ("real", "stuff") + + def test_flexmock_should_verify_correct_spy_regexp_return_values(self): + class User: + def get_stuff(self): + return "real", "stuff" + + user = User() + flexmock(user).should_call("get_stuff").and_return( + re.compile("ea.*"), re.compile("^stuff$") + ) + assert user.get_stuff() == ("real", "stuff") + + def test_flexmock_should_verify_spy_raises_correct_exception_class(self): + class FakeException(Exception): + def __init__(self, param, param2): + self.message = f"{param}, {param2}" + Exception.__init__(self) + + class User: + def get_stuff(self): + raise FakeException(1, 2) + + user = User() + flexmock(user).should_call("get_stuff").and_raise(FakeException, 1, 2) + user.get_stuff() + + def test_flexmock_should_verify_spy_matches_exception_message(self): + class FakeException(Exception): + def __init__(self, param1, param2): + self.param1 = param1 + self.param2 = param2 + Exception.__init__(self, param1) + + def __str__(self): + return f"{self.param1}, {self.param2}" + + class User: + def get_stuff(self): + raise FakeException("1", "2") + + user = User() + flexmock(user).should_call("get_stuff").and_raise(FakeException, "2", "1") + with assert_raises( + exceptions.ExceptionMessageError, + ( + "Error message mismatch with raised FakeException:\n" + " Expected message:\n\t'1, 2'\n" + " Received message:\n\t'2, 1'" + ), + ): + user.get_stuff() + + def test_flexmock_should_verify_spy_matches_exception_regexp(self): + class User: + def get_stuff(self): + raise Exception("123asdf345") + + user = User() + flexmock(user).should_call("get_stuff").and_raise(Exception, re.compile("asdf")) + user.get_stuff() + flexmock_teardown() + + def test_flexmock_should_verify_spy_matches_exception_regexp_mismatch(self): + class User: + def get_stuff(self): + raise Exception("123asdf345") + + user = User() + flexmock(user).should_call("get_stuff").and_raise(Exception, re.compile("^asdf")) + with assert_raises( + exceptions.ExceptionMessageError, + ( + "Error message mismatch with raised Exception:\n" + " Expected pattern:\n\t/^asdf/\n" + " Received message:\n\t'123asdf345'" + ), + ): + user.get_stuff() + + def test_flexmock_should_blow_up_on_wrong_spy_exception_type(self): + class User: + def get_stuff(self): + raise exceptions.CallOrderError("foo") + + user = User() + flexmock(user).should_call("get_stuff").and_raise(exceptions.MethodCallError) + with assert_raises( + exceptions.ExceptionClassError, + ( + "Raised exception for call get_stuff did not match expectation:\n" + " Expected:\t\n" + " Raised:\t" + ), + ): + user.get_stuff() + + def test_flexmock_should_match_spy_exception_parent_type(self): + class User: + def get_stuff(self): + raise exceptions.CallOrderError("foo") + + user = User() + flexmock(user).should_call("get_stuff").and_raise(exceptions.FlexmockError) + user.get_stuff() + + def test_flexmock_should_call_respects_matched_expectations(self): + class Group: + def method1(self, arg1, arg2="b"): + return f"{arg1}:{arg2}" + + def method2(self, arg): + return arg + + group = Group() + flexmock(group).should_call("method1").twice() + assert group.method1("a", arg2="c") == "a:c" + assert group.method1("a") == "a:b" + group.should_call("method2").once().with_args("c") + assert group.method2("c") == "c" + flexmock_teardown() + + def test_flexmock_should_call_respects_unmatched_expectations(self): + class Group: + def method1(self, arg1, arg2="b"): + return f"{arg1}:{arg2}" + + def method2(self, arg1): + pass + + group = Group() + flexmock(group).should_call("method1").at_least().once() + with assert_raises( + exceptions.MethodCallError, + "method1() expected to be called at least 1 time, called 0 times", + ): + flexmock_teardown() + flexmock(group) + group.should_call("method2").with_args("a").once() + group.should_receive("method2").with_args("not a") + group.method2("not a") + with assert_raises( + exceptions.MethodCallError, + 'method2(arg1="a") expected to be called exactly 1 time, called 0 times', + ): + flexmock_teardown() + + def test_flexmock_should_not_blow_up_on_should_call_for_class_methods(self): + class User: + @classmethod + def method(cls): + return "class" + + flexmock(User).should_call("method") + assert User.method() == "class" + + def test_flexmock_should_not_blow_up_on_should_call_for_static_methods(self): + class User: + @staticmethod + def method(): + return "static" + + flexmock(User).should_call("method") + assert User.method() == "static" + + def test_should_call_with_class_default_attributes(self): + """Flexmock should not allow mocking class default attributes like + __call__ on an instance. + """ + + class WithCall: + def __call__(self, arg1): + return arg1 + + instance = WithCall() + + with assert_raises( + exceptions.FlexmockError, + re.compile(r".+\.WithCall object at 0x.+> does not have attribute '__call__'"), + ): + flexmock(instance).should_call("__call__") + + def test_should_call_on_class_mock(self): + class User: + def __init__(self): + self.value = "value" + + def method1(self): + return "class" + + def method2(self): + return self.value + + # Access class-level method + user1 = User() + user2 = User() + flexmock(User).should_call("method1").once() + with assert_raises( + exceptions.MethodCallError, + "method1() expected to be called exactly 1 time, called 0 times", + ): + flexmock_teardown() + flexmock(User).should_call("method1").twice() + assert user1.method1() == "class" + assert user2.method1() == "class" + + # Access instance attributes + flexmock(User).should_call("method2").once() + with assert_raises( + exceptions.MethodCallError, + "method2() expected to be called exactly 1 time, called 0 times", + ): + flexmock_teardown() + flexmock(User).should_call("method2").twice() + assert user1.method2() == "value" + assert user2.method2() == "value" + + # Try resetting the expectation + flexmock(User).should_call("method2").once() + assert user1.method2() == "value" diff --git a/tests/features/stubbing.py b/tests/features/stubbing.py new file mode 100644 index 0000000..91154b6 --- /dev/null +++ b/tests/features/stubbing.py @@ -0,0 +1,64 @@ +"""Tests for flexmock stubbing feature.""" +# pylint: disable=missing-docstring,no-self-use,no-member +from flexmock import flexmock +from flexmock._api import flexmock_teardown +from tests import some_module + + +class StubbingTestCase: + def test_use_replace_with_for_callable_shortcut_kwargs(self): + class Foo: + def method(self): + return "bar" + + instance = Foo() + flexmock(instance, method=lambda: "baz") + assert instance.method() == "baz" + + def test_should_replace_attributes_that_are_instances_of_classes(self): + class Class1: + pass + + class Class2: + class1 = Class1() + + class2 = Class2() + flexmock(class2, class1="test") + assert class2.class1 == "test" + + def test_replace_non_callable_instance_attributes(self): + class FooClass: + def __init__(self): + self.attribute = 1 + + instance1 = FooClass() + instance2 = FooClass() + flexmock(instance1, attribute=2) + flexmock(instance2, attribute=1) + assert instance1.attribute == 2 + flexmock_teardown() + assert instance1.attribute == 1 + + def test_replace_non_callable_module_attributes(self): + flexmock(some_module, MODULE_LEVEL_ATTRIBUTE="yay") + assert some_module.MODULE_LEVEL_ATTRIBUTE == "yay" + flexmock_teardown() + assert some_module.MODULE_LEVEL_ATTRIBUTE == "test" + + def test_replace_non_callable_class_attributes(self): + class FooClass: + attribute = 1 + + instance1 = FooClass() + instance2 = FooClass() + flexmock(instance1, attribute=2) + assert instance1.attribute == 2 + assert instance2.attribute == 1 + flexmock_teardown() + assert instance1.attribute == 1 + + def test_fake_object_takes_properties(self): + fake1 = flexmock(bar=property(lambda self: "baz")) + fake2 = flexmock(foo=property(lambda self: "baz")) + assert fake1.bar == "baz" + assert fake2.foo == "baz" diff --git a/tests/features/teardown.py b/tests/features/teardown.py new file mode 100644 index 0000000..3a12697 --- /dev/null +++ b/tests/features/teardown.py @@ -0,0 +1,186 @@ +"""Tests for flexmock teardown.""" +# pylint: disable=missing-docstring,no-self-use,no-member +from flexmock import exceptions, flexmock +from flexmock._api import UPDATED_ATTRS, flexmock_teardown +from tests import some_module +from tests.utils import assert_raises + + +class TeardownTestCase: + def test_flexmock_should_properly_restore_static_methods(self): + class User: + @staticmethod + def get_stuff(): + return "ok!" + + assert isinstance(User.__dict__["get_stuff"], staticmethod) + assert User.get_stuff() == "ok!" + flexmock(User).should_receive("get_stuff") + assert User.get_stuff() is None + flexmock_teardown() + assert User.get_stuff() == "ok!" + assert isinstance(User.__dict__["get_stuff"], staticmethod) + + def test_flexmock_should_properly_restore_undecorated_static_methods(self): + class User: + def get_stuff(): + return "ok!" + + get_stuff = staticmethod(get_stuff) # pylint: disable=no-staticmethod-decorator + + assert User.get_stuff() == "ok!" + flexmock(User).should_receive("get_stuff") + assert User.get_stuff() is None + flexmock_teardown() + assert User.get_stuff() == "ok!" + + def test_flexmock_should_properly_restore_module_level_functions(self): + flexmock(some_module).should_receive("module_function").with_args(1, 2) + assert some_module.module_function(1, 2) is None + flexmock_teardown() + assert some_module.module_function(1, 2) == -1 + + def test_flexmock_should_revert_new_instances_on_teardown(self): + class User: + pass + + class Group: + pass + + user = User() + group = Group() + flexmock(Group).new_instances(user) + assert user is Group() + flexmock_teardown() + assert group.__class__ is Group().__class__ + + def test_flexmock_should_cleanup_added_methods_and_attributes(self): + class Group: + pass + + group = Group() + flexmock(Group) + assert "should_receive" in Group.__dict__ + assert "should_receive" not in group.__dict__ + flexmock(group) + assert "should_receive" in group.__dict__ + flexmock_teardown() + for method in UPDATED_ATTRS: + assert method not in Group.__dict__ + assert method not in group.__dict__ + + def test_class_attributes_are_unchanged_after_mocking(self): + class Base: + @classmethod + def class_method(cls): + pass + + @staticmethod + def static_method(): + pass + + def instance_method(self): + pass + + class Child(Base): + pass + + instance = Base() + base_attrs = list(vars(Base).keys()) + instance_attrs = list(vars(instance).keys()) + child_attrs = list(vars(Child).keys()) + flexmock(Base).should_receive("class_method").once() + flexmock(Base).should_receive("static_method").once() + Base.class_method() + Base.static_method() + + flexmock(instance).should_receive("class_method").once() + flexmock(instance).should_receive("static_method").once() + flexmock(instance).should_receive("instance_method").once() + instance.class_method() + instance.static_method() + instance.instance_method() + + flexmock(Child).should_receive("class_method").once() + flexmock(Child).should_receive("static_method").once() + Child.class_method() + Child.static_method() + + flexmock_teardown() + assert base_attrs == list(vars(Base).keys()) + assert instance_attrs == list(vars(instance).keys()) + assert child_attrs == list(vars(Child).keys()) + + def test_class_attributes_are_unchanged_after_spying(self): + class Base: + @classmethod + def class_method(cls): + pass + + @staticmethod + def static_method(): + pass + + def instance_method(self): + pass + + class Child(Base): + pass + + instance = Base() + base_attrs = list(vars(Base).keys()) + instance_attrs = list(vars(instance).keys()) + child_attrs = list(vars(Child).keys()) + flexmock(Base).should_call("class_method").times(3) # TODO: should be once #80 + flexmock(Base).should_call("static_method").times(3) # TODO: should be once #80 + Base.class_method() + Base.static_method() + + flexmock(instance).should_call("class_method").once() + flexmock(instance).should_call("static_method").once() + flexmock(instance).should_call("instance_method").once() + instance.class_method() + instance.static_method() + instance.instance_method() + + flexmock(Child).should_call("class_method").once() + flexmock(Child).should_call("static_method").once() + Child.class_method() + Child.static_method() + + flexmock_teardown() + assert base_attrs == list(vars(Base).keys()) + assert instance_attrs == list(vars(instance).keys()) + assert child_attrs == list(vars(Child).keys()) + + def test_flexmock_should_cleanup_after_exception(self): + class User: + def method2(self): + pass + + class Group: + def method1(self): + pass + + flexmock(Group) + flexmock(User) + Group.should_receive("method1").once() + User.should_receive("method2").once() + with assert_raises( + exceptions.MethodCallError, + "method1() expected to be called exactly 1 time, called 0 times", + ): + flexmock_teardown() + for method in UPDATED_ATTRS: + assert method not in dir(Group) + for method in UPDATED_ATTRS: + assert method not in dir(User) + + def test_flexmock_teardown_called_between_tests_part1(self): + flexmock().should_receive("method1").ordered() + + def test_flexmock_teardown_called_between_tests_part2(self): + mock = flexmock().should_receive("method2").ordered().mock() + # Raises CallOrderError if flexmock teardown is not automatically called + # after test part 1 above + mock.method2() diff --git a/tests/some_module.py b/tests/some_module.py index b771499..61715c4 100644 --- a/tests/some_module.py +++ b/tests/some_module.py @@ -1,5 +1,40 @@ """Bogus module for testing.""" -# pylint: disable=missing-docstring,disallowed-name,invalid-name +# pylint: disable=missing-docstring,disallowed-name,invalid-name,no-self-use + +MODULE_LEVEL_ATTRIBUTE = "test" + + +class SomeClass: + CLASS_VALUE = "class_method" + + def __init__(self): + self.instance_value = "instance_method" + + @classmethod + def class_method(cls): + return cls.CLASS_VALUE + + @classmethod + def class_method_with_args(cls, a): + return a + + @staticmethod + def static_method(): + return "static_method" + + @staticmethod + def static_method_with_args(a): + return a + + def instance_method(self): + return self.instance_value + + def instance_method_with_args(self, a): + return a + + +class DerivedClass(SomeClass): + pass class ModuleClass: diff --git a/tests/test_flexmock.py b/tests/test_flexmock.py deleted file mode 100644 index 5e89762..0000000 --- a/tests/test_flexmock.py +++ /dev/null @@ -1,3069 +0,0 @@ -"""Flexmock tests.""" -# pylint: disable=missing-docstring,too-many-lines,disallowed-name,no-member,invalid-name,no-self-use -import functools -import os -import random -import re -import sys -import unittest -from contextlib import contextmanager -from typing import Type, Union - -from flexmock._api import ( - AT_LEAST, - AT_MOST, - EXACTLY, - RE_TYPE, - UPDATED_ATTRS, - CallOrderError, - ExceptionClassError, - ExceptionMessageError, - FlexmockContainer, - FlexmockError, - MethodCallError, - MethodSignatureError, - Mock, - MockBuiltinError, - ReturnValue, - StateError, - _format_args, - _is_class_method, - _is_static_method, - _isproperty, - flexmock, - flexmock_teardown, -) -from tests import some_module - -from .proxy import Proxy - - -def module_level_function(some, args): - return f"{some}, {args}" - - -MODULE_LEVEL_ATTRIBUTE = "test" - - -class SomeClass: - CLASS_VALUE = "class_method" - - def __init__(self): - self.instance_value = "instance_method" - - @classmethod - def class_method(cls): - return cls.CLASS_VALUE - - @classmethod - def class_method_with_args(cls, a): - return a - - @staticmethod - def static_method(): - return "static_method" - - @staticmethod - def static_method_with_args(a): - return a - - def instance_method(self): - return self.instance_value - - def instance_method_with_args(self, a): - return a - - -class DerivedClass(SomeClass): - pass - - -@contextmanager -def assert_raises(expected_exception: Type[BaseException], match: Union[RE_TYPE, str, None]): - """Assert that code raises the correct exception with a correct error message. - - Args: - expected_exception: Type of the expected exception. - match: String or pattern to match the error message against. Use None - to skip error message checking. - """ - try: - yield - except Exception as raised_exception: - if not isinstance(raised_exception, expected_exception): - raise AssertionError( - f"Expected exception '{type(expected_exception)}' " - f"but '{type(raised_exception)}' was raised" - ) from raised_exception - if match is not None: - fail = False - if isinstance(match, RE_TYPE): - fail = not match.search(str(raised_exception)) - match = match.pattern - else: - fail = str(raised_exception) != str(match) - if fail: - raise AssertionError( - f"Expected error message:\n{'-'*39}\n'{str(match)}'\n" - f"\nBut got:\n\n'{str(raised_exception)}'\n{'-'*39}\n" - ) from raised_exception - else: - raise AssertionError(f"Exception '{expected_exception.__name__}' not raised") - - -def assert_equal(expected, received, msg=""): - if not msg: - msg = f"expected {expected}, received {received}" - if expected != received: - raise AssertionError(f"{expected} != {received} : {msg}") - - -class RegularClass: - def _tear_down(self): - return flexmock_teardown() - - def test_print_expectation(self): - mock = flexmock() - expectation = mock.should_receive("foo") - assert str(expectation) == "foo() -> ()" - - def test_flexmock_should_create_mock_object(self): - mock = flexmock() - assert isinstance(mock, Mock) - - def test_flexmock_should_create_mock_object_from_dict(self): - mock = flexmock(foo="foo", bar="bar") - assert_equal("foo", mock.foo) - assert_equal("bar", mock.bar) - - def test_flexmock_should_add_expectations(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo") - assert "method_foo" in [x._name for x in FlexmockContainer.flexmock_objects[mock]] - - def test_flexmock_should_return_value(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar") - mock.should_receive("method_bar").and_return("value_baz") - assert_equal("value_bar", mock.method_foo()) - assert_equal("value_baz", mock.method_bar()) - - def test_calling_deprecated_properties_should_raise_exception(self): - error_msg = "Calling once, twice, never, or mock without parentheses has been deprecated" - mock = flexmock() - mock.should_receive("foo").once # pylint: disable=expression-not-assigned - with assert_raises(FlexmockError, error_msg): - self._tear_down() - mock.should_receive("foo").twice # pylint: disable=expression-not-assigned - with assert_raises(FlexmockError, error_msg): - self._tear_down() - mock.should_receive("foo").never # pylint: disable=expression-not-assigned - with assert_raises(FlexmockError, error_msg): - self._tear_down() - mock.should_receive("foo").mock # pylint: disable=expression-not-assigned - with assert_raises(FlexmockError, error_msg): - self._tear_down() - - def test_type_flexmock_with_unicode_string_in_should_receive(self): - class Foo: - def bar(self): - return "bar" - - flexmock(Foo).should_receive("bar").and_return("mocked_bar") - - foo = Foo() - assert_equal("mocked_bar", foo.bar()) - - def test_flexmock_should_accept_shortcuts_for_creating_mock_object(self): - mock = flexmock(attr1="value 1", attr2=lambda: "returning 2") - assert_equal("value 1", mock.attr1) - assert_equal("returning 2", mock.attr2()) - - def test_flexmock_should_accept_shortcuts_for_creating_expectations(self): - class Foo: - def method1(self): - pass - - def method2(self): - pass - - foo = Foo() - flexmock(foo, method1="returning 1", method2="returning 2") - assert_equal("returning 1", foo.method1()) - assert_equal("returning 2", foo.method2()) - assert_equal("returning 2", foo.method2()) - - def test_flexmock_expectations_returns_all(self): - mock = flexmock(name="temp") - assert mock not in FlexmockContainer.flexmock_objects - mock.should_receive("method_foo") - mock.should_receive("method_bar") - assert_equal(2, len(FlexmockContainer.flexmock_objects[mock])) - - def test_flexmock_expectations_returns_named_expectation(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo") - assert_equal( - "method_foo", - FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._name, - ) - - def test_flexmock_expectations_returns_none_if_not_found(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo") - assert FlexmockContainer.get_flexmock_expectation(mock, "method_bar") is None - - def test_flexmock_should_check_parameters(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").with_args("bar").and_return(1) - mock.should_receive("method_foo").with_args("baz").and_return(2) - assert_equal(1, mock.method_foo("bar")) - assert_equal(2, mock.method_foo("baz")) - - def test_flexmock_should_keep_track_of_calls(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").with_args("foo").and_return(0) - mock.should_receive("method_foo").with_args("bar").and_return(1) - mock.should_receive("method_foo").with_args("baz").and_return(2) - mock.method_foo("bar") - mock.method_foo("bar") - mock.method_foo("baz") - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo", ("foo",)) - assert_equal(0, expectation._times_called) - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo", ("bar",)) - assert_equal(2, expectation._times_called) - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo", ("baz",)) - assert_equal(1, expectation._times_called) - - def test_flexmock_should_set_expectation_call_numbers(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").times(1) - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - with assert_raises( - MethodCallError, "method_foo() expected to be called exactly 1 time, called 0 times" - ): - expectation._verify() - mock.method_foo() - expectation._verify() - - def test_flexmock_should_check_raised_exceptions(self): - mock = flexmock(name="temp") - - class FakeException(Exception): - pass - - mock.should_receive("method_foo").and_raise(FakeException) - with assert_raises(FakeException, ""): - mock.method_foo() - assert_equal( - 1, - FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._times_called, - ) - - def test_flexmock_should_check_raised_exceptions_instance_with_args(self): - mock = flexmock(name="temp") - - class FakeException(Exception): - def __init__(self, arg, arg2): - # pylint: disable=super-init-not-called - pass - - mock.should_receive("method_foo").and_raise(FakeException(1, arg2=2)) - with assert_raises(FakeException, "1"): - mock.method_foo() - assert_equal( - 1, - FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._times_called, - ) - - def test_flexmock_should_check_raised_exceptions_class_with_args(self): - mock = flexmock(name="temp") - - class FakeException(Exception): - def __init__(self, arg, arg2): - # pylint: disable=super-init-not-called - pass - - mock.should_receive("method_foo").and_raise(FakeException, 1, arg2=2) - with assert_raises(FakeException, "1"): - mock.method_foo() - assert_equal( - 1, - FlexmockContainer.get_flexmock_expectation(mock, "method_foo")._times_called, - ) - - def test_flexmock_should_match_any_args_by_default(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("bar") - mock.should_receive("method_foo").with_args("baz").and_return("baz") - assert_equal("bar", mock.method_foo()) - assert_equal("bar", mock.method_foo(1)) - assert_equal("bar", mock.method_foo("foo", "bar")) - assert_equal("baz", mock.method_foo("baz")) - - def test_spying_non_existent_mock_object_method_should_fail(self): - mock = flexmock() - with assert_raises( - FlexmockError, - "Mock object does not have attribute 'method_foo'. " - 'Did you mean to call should_receive("method_foo") instead?', - ): - mock.should_call("method_foo") - mock = flexmock(method_foo=lambda: "ok") - mock.should_call("method_foo") - - def test_flexmock_should_fail_to_match_exactly_no_args_when_calling_with_args(self): - mock = flexmock() - mock.should_receive("method_foo").with_args() - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method_foo did not match expectations:\n" - ' Received call:\tmethod_foo("baz")\n' - " Expected call[1]:\tmethod_foo()" - ), - ): - mock.method_foo("baz") - - def test_flexmock_should_match_exactly_no_args(self): - class Foo: - def bar(self): - pass - - foo = Foo() - flexmock(foo).should_receive("bar").with_args().and_return("baz") - assert_equal("baz", foo.bar()) - - def test_expectation_dot_mock_should_return_mock(self): - mock = flexmock(name="temp") - assert_equal(mock, mock.should_receive("method_foo").mock()) - - def test_flexmock_should_create_partial_new_style_object_mock(self): - class User: - def __init__(self, name=None): - self.name = name - - def get_name(self): - return self.name - - def set_name(self, name): - self.name = name - - user = User() - flexmock(user) - user.should_receive("get_name").and_return("john") - user.set_name("mike") - assert_equal("john", user.get_name()) - - def test_flexmock_should_create_partial_old_style_object_mock(self): - class User: - def __init__(self, name=None): - self.name = name - - def get_name(self): - return self.name - - def set_name(self, name): - self.name = name - - user = User() - flexmock(user) - user.should_receive("get_name").and_return("john") - user.set_name("mike") - assert_equal("john", user.get_name()) - - def test_flexmock_should_create_partial_new_style_class_mock(self): - class User: - def __init__(self): - pass - - def get_name(self): - pass - - flexmock(User) - User.should_receive("get_name").and_return("mike") - user = User() - assert_equal("mike", user.get_name()) - - def test_flexmock_should_create_partial_old_style_class_mock(self): - class User: - def __init__(self): - pass - - def get_name(self): - pass - - flexmock(User) - User.should_receive("get_name").and_return("mike") - user = User() - assert_equal("mike", user.get_name()) - - def test_flexmock_should_match_expectations_against_builtin_classes(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").with_args(str).and_return("got a string") - mock.should_receive("method_foo").with_args(int).and_return("got an int") - assert_equal("got a string", mock.method_foo("string!")) - assert_equal("got an int", mock.method_foo(23)) - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method_foo did not match expectations:\n" - " Received call:\tmethod_foo(2.0)\n" - " Expected call[1]:\tmethod_foo()\n" - " Expected call[2]:\tmethod_foo()" - ), - ): - mock.method_foo(2.0) - - def test_with_args_should_work_with_builtin_c_methods(self): - flexmock(sys.stdout).should_call("write") # set fall-through - flexmock(sys.stdout).should_receive("write").with_args("flexmock_builtin_test") - sys.stdout.write("flexmock_builtin_test") - - def test_with_args_should_work_with_builtin_c_functions(self): - mocked = flexmock(sys) - mocked.should_receive("exit").with_args(1).once() - mocked.exit(1) - self._tear_down() - flexmock(os).should_receive("remove").with_args("path").once() - os.remove("path") - - def test_with_args_should_work_with_builtin_python_methods(self): - flexmock(random).should_receive("randint").with_args(1, 10).once() - random.randint(1, 10) - - def test_flexmock_should_match_expectations_against_user_defined_classes(self): - mock = flexmock(name="temp") - - class Foo: - pass - - mock.should_receive("method_foo").with_args(Foo).and_return("got a Foo") - assert_equal("got a Foo", mock.method_foo(Foo())) - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method_foo did not match expectations:\n" - " Received call:\tmethod_foo(1)\n" - " Expected call[1]:\tmethod_foo(.Foo'>)" - ), - ): - mock.method_foo(1) - - def test_flexmock_configures_global_mocks_dict(self): - mock = flexmock(name="temp") - assert mock not in FlexmockContainer.flexmock_objects - mock.should_receive("method_foo") - assert mock in FlexmockContainer.flexmock_objects - assert_equal(len(FlexmockContainer.flexmock_objects[mock]), 1) - - def test_flexmock_teardown_verifies_mocks(self): - mock = flexmock(name="temp") - mock.should_receive("verify_expectations").times(1) - with assert_raises( - MethodCallError, - "verify_expectations() expected to be called exactly 1 time, called 0 times", - ): - self._tear_down() - - def test_flexmock_teardown_does_not_verify_stubs(self): - mock = flexmock(name="temp") - mock.should_receive("verify_expectations") - self._tear_down() - - def test_flexmock_preserves_stubbed_object_methods_between_tests(self): - class User: - def get_name(self): - return "mike" - - user = User() - flexmock(user).should_receive("get_name").and_return("john") - assert_equal("john", user.get_name()) - self._tear_down() - assert_equal("mike", user.get_name()) - - def test_flexmock_preserves_stubbed_class_methods_between_tests(self): - class User: - def get_name(self): - return "mike" - - user = User() - flexmock(User).should_receive("get_name").and_return("john") - assert_equal("john", user.get_name()) - self._tear_down() - assert_equal("mike", user.get_name()) - - def test_flexmock_removes_new_stubs_from_objects_after_tests(self): - class User: - def get_name(self): - pass - - user = User() - saved = user.get_name - flexmock(user).should_receive("get_name").and_return("john") - assert saved != user.get_name - assert_equal("john", user.get_name()) - self._tear_down() - assert_equal(saved, user.get_name) - - def test_flexmock_removes_new_stubs_from_classes_after_tests(self): - class User: - def get_name(self): - pass - - user = User() - saved = user.get_name - flexmock(User).should_receive("get_name").and_return("john") - assert saved != user.get_name - assert_equal("john", user.get_name()) - self._tear_down() - assert_equal(saved, user.get_name) - - def test_flexmock_removes_stubs_from_multiple_objects_on_teardown(self): - class User: - def get_name(self): - pass - - class Group: - def get_name(self): - pass - - user = User() - group = Group() - saved1 = user.get_name - saved2 = group.get_name - flexmock(user).should_receive("get_name").and_return("john").once() - flexmock(group).should_receive("get_name").and_return("john").once() - assert saved1 != user.get_name - assert saved2 != group.get_name - assert_equal("john", user.get_name()) - assert_equal("john", group.get_name()) - self._tear_down() - assert_equal(saved1, user.get_name) - assert_equal(saved2, group.get_name) - - def test_flexmock_removes_stubs_from_multiple_classes_on_teardown(self): - class User: - def get_name(self): - pass - - class Group: - def get_name(self): - pass - - user = User() - group = User() - saved1 = user.get_name - saved2 = group.get_name - flexmock(User).should_receive("get_name").and_return("john") - flexmock(Group).should_receive("get_name").and_return("john") - assert saved1 != user.get_name - assert saved2 != group.get_name - assert_equal("john", user.get_name()) - assert_equal("john", group.get_name()) - self._tear_down() - assert_equal(saved1, user.get_name) - assert_equal(saved2, group.get_name) - - def test_flexmock_stubs_are_callable(self): - stub = flexmock() - mocked = flexmock() - mocked.should_receive("create").twice() - stub.tickets = mocked - # stub.tickets should work with and without parentheses - stub.tickets().create() - stub.tickets.create() - self._tear_down() - - def test_flexmock_respects_at_least_when_called_less_than_requested(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("bar").at_least().twice() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(AT_LEAST, expectation._modifier) - mock.method_foo() - with assert_raises( - MethodCallError, "method_foo() expected to be called at least 2 times, called 1 time" - ): - self._tear_down() - - def test_flexmock_respects_at_least_when_called_requested_number(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar").at_least().once() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(AT_LEAST, expectation._modifier) - mock.method_foo() - self._tear_down() - - def test_flexmock_respects_at_least_when_called_more_than_requested(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar").at_least().once() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(AT_LEAST, expectation._modifier) - mock.method_foo() - mock.method_foo() - self._tear_down() - - def test_flexmock_respects_at_most_when_called_less_than_requested(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("bar").at_most().twice() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(AT_MOST, expectation._modifier) - mock.method_foo() - self._tear_down() - - def test_flexmock_respects_at_most_when_called_requested_number(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar").at_most().once() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(AT_MOST, expectation._modifier) - mock.method_foo() - self._tear_down() - - def test_flexmock_respects_at_most_when_called_more_than_requested(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar").at_most().once() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(AT_MOST, expectation._modifier) - mock.method_foo() - with assert_raises( - MethodCallError, "method_foo() expected to be called at most 1 time, called 2 times" - ): - mock.method_foo() - - def test_flexmock_treats_once_as_times_one(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar").once() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(1, expectation._expected_calls[EXACTLY]) - with assert_raises( - MethodCallError, "method_foo() expected to be called exactly 1 time, called 0 times" - ): - self._tear_down() - - def test_flexmock_treats_twice_as_times_two(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").twice().and_return("value_bar") - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(2, expectation._expected_calls[EXACTLY]) - with assert_raises( - MethodCallError, "method_foo() expected to be called exactly 2 times, called 0 times" - ): - self._tear_down() - - def test_flexmock_works_with_never_when_true(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar").never() - expectation = FlexmockContainer.get_flexmock_expectation(mock, "method_foo") - assert_equal(0, expectation._expected_calls[EXACTLY]) - self._tear_down() - - def test_flexmock_works_with_never_when_false(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").and_return("value_bar").never() - with assert_raises( - MethodCallError, "method_foo() expected to be called exactly 0 times, called 1 time" - ): - mock.method_foo() - - def test_flexmock_get_flexmock_expectation_should_work_with_args(self): - mock = flexmock(name="temp") - mock.should_receive("method_foo").with_args("value_bar") - assert FlexmockContainer.get_flexmock_expectation(mock, "method_foo", "value_bar") - - def test_flexmock_function_should_return_previously_mocked_object(self): - class User: - pass - - user = User() - foo = flexmock(user) - assert foo == user - assert foo == flexmock(user) - - def test_flexmock_should_not_return_class_object_if_mocking_instance(self): - class User: - def method(self): - pass - - user = User() - user2 = User() - class_mock = flexmock(User).should_receive("method").and_return("class").mock() - user_mock = flexmock(user).should_receive("method").and_return("instance").mock() - assert class_mock is not user_mock - assert_equal("instance", user.method()) - assert_equal("class", user2.method()) - - def test_should_call_with_class_default_attributes(self): - """Flexmock should not allow mocking class default attributes like - __call__ on an instance. - """ - - class WithCall: - def __call__(self, a): - return a - - instance = WithCall() - - with assert_raises( - FlexmockError, - re.compile(r".+\.WithCall object at 0x.+> does not have attribute '__call__'"), - ): - flexmock(instance).should_call("__call__") - - def test_should_call_on_class_mock(self): - class User: - def __init__(self): - self.value = "value" - - def foo(self): - return "class" - - def bar(self): - return self.value - - # Access class-level method - user1 = User() - user2 = User() - flexmock(User).should_call("foo").once() - with assert_raises( - MethodCallError, "foo() expected to be called exactly 1 time, called 0 times" - ): - self._tear_down() - flexmock(User).should_call("foo").twice() - assert_equal("class", user1.foo()) - assert_equal("class", user2.foo()) - - # Access instance attributes - flexmock(User).should_call("bar").once() - with assert_raises( - MethodCallError, "bar() expected to be called exactly 1 time, called 0 times" - ): - self._tear_down() - flexmock(User).should_call("bar").twice() - assert_equal("value", user1.bar()) - assert_equal("value", user2.bar()) - - # Try resetting the expectation - flexmock(User).should_call("bar").once() - assert_equal("value", user1.bar()) - - def test_mock_proxied_class(self): - # pylint: disable=not-callable - SomeClassProxy = Proxy(SomeClass) - flexmock(SomeClassProxy).should_receive("class_method").and_return(2).twice() - assert SomeClassProxy().class_method() == 2 - assert SomeClassProxy.class_method() == 2 - flexmock(SomeClassProxy).should_receive("static_method").and_return(3).twice() - assert SomeClassProxy().static_method() == 3 - assert SomeClassProxy.static_method() == 3 - instance = SomeClassProxy() - flexmock(instance).should_receive("instance_method").and_return(4).once() - assert instance.instance_method() == 4 - - def test_mock_proxied_class_with_args(self): - # pylint: disable=not-callable - SomeClassProxy = Proxy(SomeClass) - flexmock(SomeClassProxy).should_receive("class_method_with_args").with_args("a").and_return( - 2 - ).twice() - assert SomeClassProxy().class_method_with_args("a") == 2 - assert SomeClassProxy.class_method_with_args("a") == 2 - flexmock(SomeClassProxy).should_receive("static_method_with_args").with_args( - "b" - ).and_return(3).twice() - assert SomeClassProxy().static_method_with_args("b") == 3 - assert SomeClassProxy.static_method_with_args("b") == 3 - instance = SomeClassProxy() - flexmock(instance).should_receive("instance_method_with_args").with_args("c").and_return( - 4 - ).once() - assert instance.instance_method_with_args("c") == 4 - - def test_spy_proxied_class(self): - # pylint: disable=not-callable - SomeClassProxy = Proxy(SomeClass) - flexmock(SomeClassProxy).should_call("class_method").and_return("class_method").twice() - assert SomeClassProxy().class_method() == "class_method" - assert SomeClassProxy.class_method() == "class_method" - flexmock(SomeClassProxy).should_call("static_method").and_return("static_method").twice() - assert SomeClassProxy().static_method() == "static_method" - assert SomeClassProxy.static_method() == "static_method" - instance = SomeClassProxy() - flexmock(instance).should_call("instance_method").and_return("instance_method").once() - assert instance.instance_method() == "instance_method" - - def test_spy_proxied_class_with_args(self): - # pylint: disable=not-callable - SomeClassProxy = Proxy(SomeClass) - flexmock(SomeClassProxy).should_call("class_method_with_args").with_args("a").and_return( - "a" - ).twice() - assert SomeClassProxy().class_method_with_args("a") == "a" - assert SomeClassProxy.class_method_with_args("a") == "a" - flexmock(SomeClassProxy).should_call("static_method_with_args").with_args("b").and_return( - "b" - ).twice() - assert SomeClassProxy().static_method_with_args("b") == "b" - assert SomeClassProxy.static_method_with_args("b") == "b" - instance = SomeClassProxy() - flexmock(instance).should_call("instance_method_with_args").with_args("c").and_return( - "c" - ).once() - assert instance.instance_method_with_args("c") == "c" - - def test_mock_proxied_derived_class(self): - # pylint: disable=not-callable - DerivedClassProxy = Proxy(DerivedClass) - flexmock(DerivedClassProxy).should_receive("class_method").and_return(2).twice() - assert DerivedClassProxy().class_method() == 2 - assert DerivedClassProxy.class_method() == 2 - flexmock(DerivedClassProxy).should_receive("static_method").and_return(3).twice() - assert DerivedClassProxy().static_method() == 3 - assert DerivedClassProxy.static_method() == 3 - instance = DerivedClassProxy() - flexmock(instance).should_receive("instance_method").and_return(4).once() - assert instance.instance_method() == 4 - - def test_mock_proxied_module_function(self): - # pylint: disable=not-callable - some_module_proxy = Proxy(some_module) - flexmock(some_module_proxy).should_receive("module_function").and_return(3).once() - assert some_module_proxy.module_function() == 3 - - def test_spy_proxied_module_function(self): - # pylint: disable=not-callable - some_module_proxy = Proxy(some_module) - flexmock(some_module_proxy).should_receive("module_function").and_return(0).once() - assert some_module_proxy.module_function(2, 2) == 0 - - def test_mock_proxied_derived_class_with_args(self): - # pylint: disable=not-callable - DerivedClassProxy = Proxy(DerivedClass) - flexmock(DerivedClassProxy).should_receive("class_method_with_args").with_args( - "a" - ).and_return(2).twice() - assert DerivedClassProxy().class_method_with_args("a") == 2 - assert DerivedClassProxy.class_method_with_args("a") == 2 - flexmock(DerivedClassProxy).should_receive("static_method_with_args").with_args( - "b" - ).and_return(3).twice() - assert DerivedClassProxy().static_method_with_args("b") == 3 - assert DerivedClassProxy.static_method_with_args("b") == 3 - instance = DerivedClassProxy() - flexmock(instance).should_receive("instance_method_with_args").with_args("c").and_return( - 4 - ).once() - assert instance.instance_method_with_args("c") == 4 - - def test_spy_proxied_derived_class(self): - # pylint: disable=not-callable - DerivedClassProxy = Proxy(DerivedClass) - flexmock(DerivedClassProxy).should_call("class_method").and_return("class_method").twice() - assert DerivedClassProxy().class_method() == "class_method" - assert DerivedClassProxy.class_method() == "class_method" - flexmock(DerivedClassProxy).should_call("static_method").and_return("static_method").twice() - assert DerivedClassProxy().static_method() == "static_method" - assert DerivedClassProxy.static_method() == "static_method" - instance = DerivedClassProxy() - flexmock(instance).should_call("instance_method").and_return("instance_method").once() - assert instance.instance_method() == "instance_method" - - def test_spy_proxied_derived_class_with_args(self): - # pylint: disable=not-callable - DerivedClassProxy = Proxy(DerivedClass) - flexmock(DerivedClassProxy).should_call("class_method_with_args").with_args("a").and_return( - "a" - ).twice() - assert DerivedClassProxy().class_method_with_args("a") == "a" - assert DerivedClassProxy.class_method_with_args("a") == "a" - flexmock(DerivedClassProxy).should_call("static_method_with_args").with_args( - "b" - ).and_return("b").twice() - assert DerivedClassProxy().static_method_with_args("b") == "b" - assert DerivedClassProxy.static_method_with_args("b") == "b" - instance = DerivedClassProxy() - flexmock(instance).should_call("instance_method_with_args").with_args("c").and_return( - "c" - ).once() - assert instance.instance_method_with_args("c") == "c" - - def test_with_args_with_instance_method(self): - flexmock(SomeClass).should_receive("instance_method_with_args").with_args("red").once() - flexmock(SomeClass).should_receive("instance_method_with_args").with_args("blue").once() - instance = SomeClass() - instance.instance_method_with_args("red") - instance.instance_method_with_args("blue") - - def test_with_args_with_class_method(self): - flexmock(SomeClass).should_receive("class_method_with_args").with_args("red").once() - flexmock(SomeClass).should_receive("class_method_with_args").with_args("blue").once() - SomeClass.class_method_with_args("red") - SomeClass.class_method_with_args("blue") - - def test_with_args_with_static_method(self): - flexmock(SomeClass).should_receive("static_method_with_args").with_args("red").once() - flexmock(SomeClass).should_receive("static_method_with_args").with_args("blue").once() - SomeClass.static_method_with_args("red") - SomeClass.static_method_with_args("blue") - - def test_mock_class_method_on_derived_class(self): - flexmock(DerivedClass).should_receive("class_method").and_return(2).twice() - assert DerivedClass().class_method() == 2 - assert DerivedClass.class_method() == 2 - - def test_mock_class_method_on_derived_class_after_mocking_base_class(self): - flexmock(SomeClass).should_receive("class_method").and_return(1).once() - assert SomeClass.class_method() == 1 - flexmock(DerivedClass).should_receive("class_method").and_return(2).twice() - assert DerivedClass().class_method() == 2 - assert DerivedClass.class_method() == 2 - - def test_mock_static_method_on_derived_class(self): - flexmock(DerivedClass).should_receive("static_method").and_return(4).twice() - assert DerivedClass().static_method() == 4 - assert DerivedClass.static_method() == 4 - - def test_mock_static_method_on_derived_class_after_mocking_base_class(self): - flexmock(SomeClass).should_receive("static_method").and_return(3).once() - assert SomeClass.static_method() == 3 - flexmock(DerivedClass).should_receive("static_method").and_return(4).twice() - assert DerivedClass().static_method() == 4 - assert DerivedClass.static_method() == 4 - - def test_mock_class_method_with_args_on_derived_class(self): - flexmock(DerivedClass).should_receive("class_method_with_args").with_args(2).and_return( - 3 - ).twice() - assert DerivedClass().class_method_with_args(2) == 3 - assert DerivedClass.class_method_with_args(2) == 3 - - def test_mock_class_method_with_args_on_derived_class_after_mocking_base_class(self): - flexmock(SomeClass).should_receive("class_method_with_args").with_args(1).and_return( - 2 - ).once() - assert SomeClass.class_method_with_args(1) == 2 - flexmock(DerivedClass).should_receive("class_method_with_args").with_args(2).and_return( - 3 - ).twice() - assert DerivedClass().class_method_with_args(2) == 3 - assert DerivedClass.class_method_with_args(2) == 3 - - def test_mock_static_method_with_args_on_derived_class(self): - flexmock(DerivedClass).should_receive("static_method_with_args").with_args(4).and_return( - 5 - ).twice() - assert DerivedClass().static_method_with_args(4) == 5 - assert DerivedClass.static_method_with_args(4) == 5 - - def test_mock_static_method_with_args_on_derived_class_after_mocking_base_class(self): - flexmock(SomeClass).should_receive("static_method_with_args").with_args(2).and_return( - 3 - ).once() - assert SomeClass.static_method_with_args(2) == 3 - flexmock(DerivedClass).should_receive("static_method_with_args").with_args(4).and_return( - 5 - ).twice() - assert DerivedClass().static_method_with_args(4) == 5 - assert DerivedClass.static_method_with_args(4) == 5 - - def test_spy_class_method_on_derived_class(self): - flexmock(DerivedClass).should_call("class_method").and_return("class_method").twice() - assert DerivedClass().class_method() == "class_method" - assert DerivedClass.class_method() == "class_method" - - def test_spy_class_method_on_derived_class_after_spying_base_class(self): - flexmock(SomeClass).should_call("class_method").and_return("class_method").times( - 3 - ) # TODO: Should be once #80 - assert SomeClass.class_method() == "class_method" - flexmock(DerivedClass).should_call("class_method").and_return("class_method").twice() - assert DerivedClass().class_method() == "class_method" - assert DerivedClass.class_method() == "class_method" - - def test_spy_static_method_on_derived_class(self): - flexmock(DerivedClass).should_call("static_method").and_return("static_method").twice() - assert DerivedClass().static_method() == "static_method" - assert DerivedClass.static_method() == "static_method" - - def test_spy_static_method_on_derived_class_after_spying_base_class(self): - flexmock(SomeClass).should_call("static_method").and_return("static_method").times( - 3 - ) # TODO: Should be once #80 - assert SomeClass.static_method() == "static_method" - flexmock(DerivedClass).should_call("static_method").and_return("static_method").twice() - assert DerivedClass().static_method() == "static_method" - assert DerivedClass.static_method() == "static_method" - - def test_spy_class_method_with_args_on_derived_class(self): - flexmock(DerivedClass).should_call("class_method_with_args").with_args(2).and_return(2) - assert DerivedClass().class_method_with_args(2) == 2 - assert DerivedClass.class_method_with_args(2) == 2 - - @assert_raises(MethodSignatureError, match=None) # TODO: Should not raise exception #79 - def test_spy_class_method_with_args_on_derived_class_after_spying_base_class(self): - flexmock(SomeClass).should_call("class_method_with_args").with_args(1).and_return(1) - assert SomeClass.class_method_with_args(1) == 1 - flexmock(DerivedClass).should_call("class_method_with_args").with_args(2).and_return(2) - assert DerivedClass().class_method_with_args(2) == 2 - assert DerivedClass.class_method_with_args(2) == 2 - - def test_spy_static_method_with_args_on_derived_class(self): - flexmock(DerivedClass).should_call("static_method_with_args").with_args(4).and_return( - 4 - ).twice() - assert DerivedClass().static_method_with_args(4) == 4 - assert DerivedClass.static_method_with_args(4) == 4 - - @assert_raises(MethodSignatureError, match=None) # TODO: Should not raise exception #79 - def test_spy_static_method_with_args_on_derived_class_after_spying_base_class(self): - flexmock(SomeClass).should_call("static_method_with_args").with_args(2).and_return(2).once() - assert SomeClass.static_method_with_args(2) == 2 - flexmock(DerivedClass).should_call("static_method_with_args").with_args(4).and_return( - 4 - ).once() # should be twice - assert DerivedClass().static_method_with_args(4) == 4 - assert DerivedClass.static_method_with_args(4) == 4 - - def test_flexmock_should_not_blow_up_on_should_call_for_class_methods(self): - class User: - @classmethod - def foo(cls): - return "class" - - flexmock(User).should_call("foo") - assert_equal("class", User.foo()) - - def test_flexmock_should_not_blow_up_on_should_call_for_static_methods(self): - class User: - @staticmethod - def foo(): - return "static" - - flexmock(User).should_call("foo") - assert_equal("static", User.foo()) - - def test_flexmock_should_mock_new_instances_with_multiple_params(self): - class User: - pass - - class Group: - def __init__(self, arg, arg2): - pass - - user = User() - flexmock(Group).new_instances(user) - assert user is Group(1, 2) - - def test_flexmock_should_revert_new_instances_on_teardown(self): - class User: - pass - - class Group: - pass - - user = User() - group = Group() - flexmock(Group).new_instances(user) - assert user is Group() - self._tear_down() - assert_equal(group.__class__, Group().__class__) - - def test_flexmock_should_cleanup_added_methods_and_attributes(self): - class Group: - pass - - group = Group() - flexmock(Group) - assert "should_receive" in Group.__dict__ - assert "should_receive" not in group.__dict__ - flexmock(group) - assert "should_receive" in group.__dict__ - self._tear_down() - for method in UPDATED_ATTRS: - assert method not in Group.__dict__ - assert method not in group.__dict__ - - def test_class_attributes_are_unchanged_after_mocking(self): - class Base: - @classmethod - def class_method(cls): - pass - - @staticmethod - def static_method(): - pass - - def instance_method(self): - pass - - class Child(Base): - pass - - instance = Base() - base_attrs = list(vars(Base).keys()) - instance_attrs = list(vars(instance).keys()) - child_attrs = list(vars(Child).keys()) - flexmock(Base).should_receive("class_method").once() - flexmock(Base).should_receive("static_method").once() - Base.class_method() - Base.static_method() - - flexmock(instance).should_receive("class_method").once() - flexmock(instance).should_receive("static_method").once() - flexmock(instance).should_receive("instance_method").once() - instance.class_method() - instance.static_method() - instance.instance_method() - - flexmock(Child).should_receive("class_method").once() - flexmock(Child).should_receive("static_method").once() - Child.class_method() - Child.static_method() - - self._tear_down() - assert base_attrs == list(vars(Base).keys()) - assert instance_attrs == list(vars(instance).keys()) - assert child_attrs == list(vars(Child).keys()) - - def test_class_attributes_are_unchanged_after_spying(self): - class Base: - @classmethod - def class_method(cls): - pass - - @staticmethod - def static_method(): - pass - - def instance_method(self): - pass - - class Child(Base): - pass - - instance = Base() - base_attrs = list(vars(Base).keys()) - instance_attrs = list(vars(instance).keys()) - child_attrs = list(vars(Child).keys()) - flexmock(Base).should_call("class_method").times(3) # TODO: should be once #80 - flexmock(Base).should_call("static_method").times(3) # TODO: should be once #80 - Base.class_method() - Base.static_method() - - flexmock(instance).should_call("class_method").once() - flexmock(instance).should_call("static_method").once() - flexmock(instance).should_call("instance_method").once() - instance.class_method() - instance.static_method() - instance.instance_method() - - flexmock(Child).should_call("class_method").once() - flexmock(Child).should_call("static_method").once() - Child.class_method() - Child.static_method() - - self._tear_down() - assert base_attrs == list(vars(Base).keys()) - assert instance_attrs == list(vars(instance).keys()) - assert child_attrs == list(vars(Child).keys()) - - def test_flexmock_should_cleanup_after_exception(self): - class User: - def method2(self): - pass - - class Group: - def method1(self): - pass - - flexmock(Group) - flexmock(User) - Group.should_receive("method1").once() - User.should_receive("method2").once() - with assert_raises( - MethodCallError, "method1() expected to be called exactly 1 time, called 0 times" - ): - self._tear_down() - for method in UPDATED_ATTRS: - assert method not in dir(Group) - for method in UPDATED_ATTRS: - assert method not in dir(User) - - def test_flexmock_should_call_respects_matched_expectations(self): - class Group: - def method1(self, arg1, arg2="b"): - return f"{arg1}:{arg2}" - - def method2(self, arg): - return arg - - group = Group() - flexmock(group).should_call("method1").twice() - assert_equal("a:c", group.method1("a", arg2="c")) - assert_equal("a:b", group.method1("a")) - group.should_call("method2").once().with_args("c") - assert_equal("c", group.method2("c")) - self._tear_down() - - def test_flexmock_should_call_respects_unmatched_expectations(self): - class Group: - def method1(self, arg1, arg2="b"): - return f"{arg1}:{arg2}" - - def method2(self, a): - pass - - group = Group() - flexmock(group).should_call("method1").at_least().once() - with assert_raises( - MethodCallError, "method1() expected to be called at least 1 time, called 0 times" - ): - self._tear_down() - flexmock(group) - group.should_call("method2").with_args("a").once() - group.should_receive("method2").with_args("not a") - group.method2("not a") - with assert_raises( - MethodCallError, 'method2(a="a") expected to be called exactly 1 time, called 0 times' - ): - self._tear_down() - - def test_flexmock_doesnt_error_on_properly_ordered_expectations(self): - class Foo: - def foo(self): - pass - - def method1(self, a): - pass - - def bar(self): - pass - - def baz(self): - pass - - foo = Foo() - flexmock(foo).should_receive("foo") - flexmock(foo).should_receive("method1").with_args("a").ordered() - flexmock(foo).should_receive("bar") - flexmock(foo).should_receive("method1").with_args("b").ordered() - flexmock(foo).should_receive("baz") - foo.bar() - foo.method1("a") - foo.method1("b") - foo.baz() - foo.foo() - - def test_flexmock_errors_on_improperly_ordered_expectations(self): - class Foo: - def method1(self, a): - pass - - foo = Foo() - flexmock(foo) - foo.should_receive("method1").with_args("a").ordered() - foo.should_receive("method1").with_args("b").ordered() - with assert_raises(CallOrderError, 'method1("b") called before method1(a="a")'): - foo.method1("b") - - def test_flexmock_should_accept_multiple_return_values(self): - class Foo: - def method1(self): - pass - - foo = Foo() - flexmock(foo).should_receive("method1").and_return(1, 5).and_return(2) - assert_equal((1, 5), foo.method1()) - assert_equal(2, foo.method1()) - assert_equal((1, 5), foo.method1()) - assert_equal(2, foo.method1()) - - def test_flexmock_should_accept_multiple_return_values_with_shortcut(self): - class Foo: - def method1(self): - pass - - foo = Foo() - flexmock(foo).should_receive("method1").and_return(1, 2).one_by_one() - assert_equal(1, foo.method1()) - assert_equal(2, foo.method1()) - assert_equal(1, foo.method1()) - assert_equal(2, foo.method1()) - - def test_flexmock_should_accept_multiple_return_values_with_one_by_one(self): - mocked = flexmock() - flexmock(mocked).should_receive("method1").and_return(2).and_return(3).one_by_one() - assert_equal(2, mocked.method1()) - assert_equal(3, mocked.method1()) - assert_equal(2, mocked.method1()) - assert_equal(3, mocked.method1()) - - def test_one_by_one_called_before_and_return_multiple_values(self): - mocked = flexmock() - mocked.should_receive("method1").one_by_one().and_return(3, 4) - assert_equal(3, mocked.method1()) - assert_equal(4, mocked.method1()) - assert_equal(3, mocked.method1()) - assert_equal(4, mocked.method1()) - - def test_one_by_one_called_before_and_return_one_value(self): - mocked = flexmock() - mocked.should_receive("method1").one_by_one().and_return(4).and_return(5) - assert_equal(4, mocked.method1()) - assert_equal(5, mocked.method1()) - assert_equal(4, mocked.method1()) - assert_equal(5, mocked.method1()) - - def test_flexmock_should_mix_multiple_return_values_with_exceptions(self): - class Foo: - def method1(self): - pass - - foo = Foo() - flexmock(foo).should_receive("method1").and_return(1).and_raise(Exception) - assert_equal(1, foo.method1()) - with assert_raises(Exception, ""): - foo.method1() - assert_equal(1, foo.method1()) - with assert_raises(Exception, ""): - foo.method1() - - def test_flexmock_should_match_types_on_multiple_arguments(self): - class Foo: - def method1(self, a, b): - pass - - foo = Foo() - flexmock(foo).should_receive("method1").with_args(str, int).and_return("ok") - assert_equal("ok", foo.method1("some string", 12)) - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - " Received call:\tmethod1(12, 32)\n" - " Expected call[1]:\tmethod1(a=, b=)" - ), - ): - foo.method1(12, 32) - flexmock(foo).should_receive("method1").with_args(str, int).and_return("ok") - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - ' Received call:\tmethod1(12, "some string")\n' - " Expected call[1]:\tmethod1(a=, b=)\n" - " Expected call[2]:\tmethod1(a=, b=)" - ), - ): - foo.method1(12, "some string") - flexmock(foo).should_receive("method1").with_args(str, int).and_return("ok") - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - ' Received call:\tmethod1("string", 12, 14)\n' - " Expected call[1]:\tmethod1(a=, b=)\n" - " Expected call[2]:\tmethod1(a=, b=)\n" - " Expected call[3]:\tmethod1(a=, b=)" - ), - ): - foo.method1("string", 12, 14) - - def test_flexmock_should_match_types_on_multiple_arguments_generic(self): - class Foo: - def method1(self, a, b, c): - pass - - foo = Foo() - flexmock(foo).should_receive("method1").with_args(object, object, object).and_return("ok") - assert_equal("ok", foo.method1("some string", None, 12)) - assert_equal("ok", foo.method1((1,), None, 12)) - assert_equal("ok", foo.method1(12, 14, [])) - assert_equal("ok", foo.method1("some string", "another one", False)) - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - ' Received call:\tmethod1("string", 12)\n' - " Expected call[1]:\tmethod1(a=, " - "b=, c=)" - ), - ): - foo.method1("string", 12) # pylint: disable=no-value-for-parameter - self._tear_down() - flexmock(foo).should_receive("method1").with_args(object, object, object).and_return("ok") - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - ' Received call:\tmethod1("string", 12, 13, 14)\n' - " Expected call[1]:\tmethod1(a=, " - "b=, c=)" - ), - ): - foo.method1("string", 12, 13, 14) - - def test_flexmock_should_match_types_on_multiple_arguments_classes(self): - class Foo: - def method1(self, a, b): - pass - - class Bar: - pass - - foo = Foo() - bar = Bar() - flexmock(foo).should_receive("method1").with_args(object, Bar).and_return("ok") - assert_equal("ok", foo.method1("some string", bar)) - with assert_raises( - MethodSignatureError, - re.compile( - "Arguments for call method1 did not match expectations:\n" - r' Received call:\tmethod1\(.+\.\.Bar object at 0x.+>, "some string"\)\n' - r" Expected call\[1\]:\tmethod1\(a=, b=\.Bar'>\)" - ), - ): - foo.method1(bar, "some string") - self._tear_down() - flexmock(foo).should_receive("method1").with_args(object, Bar).and_return("ok") - with assert_raises( - MethodSignatureError, - re.compile( - "Arguments for call method1 did not match expectations:\n" - r' Received call:\tmethod1\(12, "some string"\)\n' - r" Expected call\[1\]:\tmethod1\(a=, b=\.Bar'>\)" - ), - ): - foo.method1(12, "some string") - - def test_flexmock_should_match_keyword_arguments(self): - class Foo: - def method1(self, a, **kwargs): - pass - - foo = Foo() - flexmock(foo).should_receive("method1").with_args(1, arg3=3, arg2=2).twice() - foo.method1(1, arg2=2, arg3=3) - foo.method1(1, arg3=3, arg2=2) - self._tear_down() - flexmock(foo).should_receive("method1").with_args(1, arg3=3, arg2=2) - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - " Received call:\tmethod1(arg2=2, arg3=3)\n" - " Expected call[1]:\tmethod1(arg3=3, arg2=2, a=1)" - ), - ): - foo.method1(arg2=2, arg3=3) # pylint: disable=no-value-for-parameter - self._tear_down() - flexmock(foo).should_receive("method1").with_args(1, arg3=3, arg2=2) - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - " Received call:\tmethod1(1, arg2=2, arg3=4)\n" - " Expected call[1]:\tmethod1(arg3=3, arg2=2, a=1)" - ), - ): - foo.method1(1, arg2=2, arg3=4) - self._tear_down() - flexmock(foo).should_receive("method1").with_args(1, arg3=3, arg2=2) - with assert_raises( - MethodSignatureError, - ( - "Arguments for call method1 did not match expectations:\n" - " Received call:\tmethod1(1)\n" - " Expected call[1]:\tmethod1(arg3=3, arg2=2, a=1)" - ), - ): - foo.method1(1) - - def test_flexmock_should_call_should_match_keyword_arguments(self): - class Foo: - def method1(self, arg1, arg2=None, arg3=None): - return f"{arg1}{arg2}{arg3}" - - foo = Foo() - flexmock(foo).should_call("method1").with_args(1, arg3=3, arg2=2).once() - assert_equal("123", foo.method1(1, arg2=2, arg3=3)) - - def test_flexmock_should_mock_private_methods(self): - class Foo: - def __private_method(self): - return "foo" - - def public_method(self): - return self.__private_method() - - foo = Foo() - flexmock(foo).should_receive("__private_method").and_return("bar") - assert_equal("bar", foo.public_method()) - - def test_flexmock_should_mock_special_methods(self): - class Foo: - def __special_method__(self): - return "foo" - - def public_method(self): - return self.__special_method__() - - foo = Foo() - flexmock(foo).should_receive("__special_method__").and_return("bar") - assert_equal("bar", foo.public_method()) - - def test_flexmock_should_mock_double_underscore_method(self): - class Foo: - def __(self): - return "foo" - - def public_method(self): - return self.__() - - foo = Foo() - flexmock(foo).should_receive("__").and_return("bar") - assert_equal("bar", foo.public_method()) - - def test_flexmock_should_mock_private_class_methods(self): - class Foo: - def __iter__(self): - pass - - flexmock(Foo).should_receive("__iter__").and_yield(1, 2, 3) - assert_equal([1, 2, 3], list(Foo())) - - def test_flexmock_should_yield_self_when_iterated(self): - class ClassNoIter: - pass - - foo = flexmock(ClassNoIter) - assert foo is list(foo)[0] - - def test_flexmock_should_mock_iter_on_new_style_instances(self): - class Foo: - def __iter__(self): - yield None - - old = Foo.__iter__ - foo = Foo() - foo2 = Foo() - foo3 = Foo() - flexmock(foo, __iter__=iter([1, 2, 3])) - flexmock(foo2, __iter__=iter([3, 4, 5])) - assert_equal([1, 2, 3], list(foo)) - assert_equal([3, 4, 5], list(foo2)) - assert_equal([None], list(foo3)) - assert_equal(False, foo.__iter__ is old) - assert_equal(False, foo2.__iter__ is old) - assert_equal(False, foo3.__iter__ is old) - self._tear_down() - assert_equal([None], list(foo)) - assert_equal([None], list(foo2)) - assert_equal([None], list(foo3)) - assert_equal(True, Foo.__iter__ == old, f"{Foo.__iter__} != {old}") - - def test_flexmock_should_mock_private_methods_with_leading_underscores(self): - class ClassWithPrivateMethods: - def __private_instance_method(self): - pass - - @classmethod - def __private_class_method(cls): - # pylint: disable=unused-private-member - # pylint bug https://github.com/PyCQA/pylint/issues/4681 - pass - - def instance_method(self): - return self.__private_instance_method() - - @classmethod - def class_method(cls): - return cls.__private_class_method() - - # Instance - instance = ClassWithPrivateMethods() - flexmock(instance).should_receive("__private_instance_method").and_return("bar") - assert_equal("bar", instance.instance_method()) - - # Class - flexmock(ClassWithPrivateMethods).should_receive("__private_class_method").and_return("baz") - assert_equal("baz", ClassWithPrivateMethods.class_method()) - - def test_flexmock_should_mock_generators(self): - class Gen: - def foo(self): - pass - - gen = Gen() - flexmock(gen).should_receive("foo").and_yield(*range(1, 10)) - output = list(gen.foo()) - assert_equal(list(range(1, 10)), output) - - def test_flexmock_should_verify_correct_spy_return_values(self): - class User: - def get_stuff(self): - return "real", "stuff" - - user = User() - flexmock(user).should_call("get_stuff").and_return("real", "stuff") - assert_equal(("real", "stuff"), user.get_stuff()) - - def test_flexmock_should_verify_correct_spy_regexp_return_values(self): - class User: - def get_stuff(self): - return "real", "stuff" - - user = User() - flexmock(user).should_call("get_stuff").and_return( - re.compile("ea.*"), re.compile("^stuff$") - ) - assert_equal(("real", "stuff"), user.get_stuff()) - - def test_flexmock_should_verify_spy_raises_correct_exception_class(self): - class FakeException(Exception): - def __init__(self, param, param2): - self.message = f"{param}, {param2}" - Exception.__init__(self) - - class User: - def get_stuff(self): - raise FakeException(1, 2) - - user = User() - flexmock(user).should_call("get_stuff").and_raise(FakeException, 1, 2) - user.get_stuff() - - def test_flexmock_should_verify_spy_matches_exception_message(self): - class FakeException(Exception): - def __init__(self, param, param2): - self.p1 = param - self.p2 = param2 - Exception.__init__(self, param) - - def __str__(self): - return f"{self.p1}, {self.p2}" - - class User: - def get_stuff(self): - raise FakeException("1", "2") - - user = User() - flexmock(user).should_call("get_stuff").and_raise(FakeException, "2", "1") - with assert_raises( - ExceptionMessageError, - ( - "Error message mismatch with raised FakeException:\n" - " Expected message:\n\t'1, 2'\n" - " Received message:\n\t'2, 1'" - ), - ): - user.get_stuff() - - def test_flexmock_should_verify_spy_matches_exception_regexp(self): - class User: - def get_stuff(self): - raise Exception("123asdf345") - - user = User() - flexmock(user).should_call("get_stuff").and_raise(Exception, re.compile("asdf")) - user.get_stuff() - self._tear_down() - - def test_flexmock_should_verify_spy_matches_exception_regexp_mismatch(self): - class User: - def get_stuff(self): - raise Exception("123asdf345") - - user = User() - flexmock(user).should_call("get_stuff").and_raise(Exception, re.compile("^asdf")) - with assert_raises( - ExceptionMessageError, - ( - "Error message mismatch with raised Exception:\n" - " Expected pattern:\n\t/^asdf/\n" - " Received message:\n\t'123asdf345'" - ), - ): - user.get_stuff() - - def test_flexmock_should_blow_up_on_wrong_spy_exception_type(self): - class User: - def get_stuff(self): - raise CallOrderError("foo") - - user = User() - flexmock(user).should_call("get_stuff").and_raise(MethodCallError) - with assert_raises( - ExceptionClassError, - ( - "Raised exception for call get_stuff did not match expectation:\n" - " Expected:\t\n" - " Raised:\t" - ), - ): - user.get_stuff() - - def test_flexmock_should_match_spy_exception_parent_type(self): - class User: - def get_stuff(self): - raise CallOrderError("foo") - - user = User() - flexmock(user).should_call("get_stuff").and_raise(FlexmockError) - user.get_stuff() - - def test_flexmock_should_reraise_exception_in_spy(self): - class RaisesException: - @classmethod - def class_method(cls): - raise RuntimeError("foo") - - @staticmethod - def static_method(): - raise RuntimeError("bar") - - def instance_method(self): - raise RuntimeError("baz") - - instance = RaisesException() - flexmock(instance).should_call("class_method").once() - flexmock(instance).should_call("static_method").once() - flexmock(instance).should_call("instance_method").once() - - with assert_raises(RuntimeError, "foo"): - instance.class_method() - with assert_raises(RuntimeError, "bar"): - instance.static_method() - with assert_raises(RuntimeError, "baz"): - instance.instance_method() - - flexmock(RaisesException).should_call("class_method").once() - flexmock(RaisesException).should_call("static_method").once() - with assert_raises(RuntimeError, "foo"): - RaisesException.class_method() - with assert_raises(RuntimeError, "bar"): - RaisesException.static_method() - - def test_flexmock_should_reraise_exception_in_spy_with_return_values(self): - class RaisesException: - @classmethod - def class_method(cls): - raise RuntimeError("foo") - - @staticmethod - def static_method(): - raise RuntimeError("bar") - - def instance_method(self): - raise RuntimeError("baz") - - instance = RaisesException() - flexmock(instance).should_call("class_method").and_return("foo").once() - flexmock(instance).should_call("static_method").and_return("bar").once() - flexmock(instance).should_call("instance_method").and_return("baz").once() - - with assert_raises(RuntimeError, "foo"): - instance.class_method() - with assert_raises(RuntimeError, "bar"): - instance.static_method() - with assert_raises(RuntimeError, "baz"): - instance.instance_method() - - flexmock(RaisesException).should_call("class_method").once() - flexmock(RaisesException).should_call("static_method").once() - with assert_raises(RuntimeError, "foo"): - RaisesException.class_method() - with assert_raises(RuntimeError, "bar"): - RaisesException.static_method() - - def test_and_raise_with_invalid_arguments(self): - with assert_raises( - FlexmockError, "can't initialize with the given arguments" - ): - flexmock().should_receive("foo").and_raise(Exception, 1, bar=2) - - def test_and_raise_with_value_that_is_not_a_class(self): - class RaisesException: - def get_stuff(self): - raise RuntimeError("baz") - - instance = RaisesException() - flexmock(instance).should_call("get_stuff").and_raise(RuntimeError("foo")).once() - with assert_raises( - ExceptionClassError, - re.compile( - r"Raised exception for call get_stuff did not match expectation:\n" - # Python 3.6 contains comma after 'foo' - r" Expected:\tRuntimeError\('foo',?\)\n" - r" Raised:\t\n\n" - r"Did you try to call and_raise with an instance\?\n" - r'Instead of and_raise\(Exception\("arg"\)\), try and_raise\(Exception, "arg"\)', - ), - ): - instance.get_stuff() - - def test_flexmock_should_blow_up_on_wrong_spy_return_values(self): - class User: - def get_stuff(self): - return "real", "stuff" - - def get_more_stuff(self): - return "other", "stuff" - - user = User() - flexmock(user).should_call("get_stuff").and_return("other", "stuff") - with assert_raises( - MethodSignatureError, - ( - "Returned values for call get_stuff did not match expectation:\n" - " Expected:\t('other', 'stuff')\n" - " Returned:\t('real', 'stuff')" - ), - ): - user.get_stuff() - flexmock(user).should_call("get_more_stuff").and_return() - with assert_raises( - MethodSignatureError, - ( - "Returned values for call get_more_stuff did not match expectation:\n" - " Expected:\tNone\n" - " Returned:\t('other', 'stuff')" - ), - ): - user.get_more_stuff() - - def test_flexmock_should_mock_same_class_twice(self): - class Foo: - pass - - flexmock(Foo) - flexmock(Foo) - - def test_flexmock_spy_should_not_clobber_original_method(self): - class User: - def get_stuff(self): - return "real", "stuff" - - user = User() - flexmock(user).should_call("get_stuff") - flexmock(user).should_call("get_stuff") - assert_equal(("real", "stuff"), user.get_stuff()) - - def test_flexmock_should_properly_restore_static_methods(self): - class User: - @staticmethod - def get_stuff(): - return "ok!" - - assert isinstance(User.__dict__["get_stuff"], staticmethod) - assert_equal("ok!", User.get_stuff()) - flexmock(User).should_receive("get_stuff") - assert User.get_stuff() is None - self._tear_down() - assert_equal("ok!", User.get_stuff()) - assert isinstance(User.__dict__["get_stuff"], staticmethod) - - def test_flexmock_should_properly_restore_undecorated_static_methods(self): - class User: - def get_stuff(): - return "ok!" - - get_stuff = staticmethod(get_stuff) # pylint: disable=no-staticmethod-decorator - - assert_equal("ok!", User.get_stuff()) - flexmock(User).should_receive("get_stuff") - assert User.get_stuff() is None - self._tear_down() - assert_equal("ok!", User.get_stuff()) - - def test_flexmock_should_properly_restore_module_level_functions(self): - if "tests.test_flexmock" in sys.modules: - mod = sys.modules["tests.test_flexmock"] - else: - mod = sys.modules["__main__"] - flexmock(mod).should_receive("module_level_function").with_args(1, 2) - assert_equal(None, module_level_function(1, 2)) - self._tear_down() - assert_equal("1, 2", module_level_function(1, 2)) - - def test_module_level_function_with_kwargs(self): - if "tests.test_flexmock" in sys.modules: - mod = sys.modules["tests.test_flexmock"] - else: - mod = sys.modules["__main__"] - flexmock(mod).should_receive("module_level_function").with_args(1, args="expected") - with assert_raises( - FlexmockError, - ( - "Arguments for call module_level_function did not match expectations:\n" - ' Received call:\tmodule_level_function(1, args="not expected")\n' - ' Expected call[1]:\tmodule_level_function(args="expected", some=1)' - ), - ): - module_level_function(1, args="not expected") - - def test_flexmock_should_support_mocking_classes_as_functions(self): - if "tests.test_flexmock" in sys.modules: - mod = sys.modules["tests.test_flexmock"] - else: - mod = sys.modules["__main__"] - flexmock(mod).should_receive("SomeClass").and_return("yay") - assert_equal("yay", SomeClass()) - - def test_flexmock_should_properly_restore_class_methods(self): - class User: - @classmethod - def get_stuff(cls): - return cls.__name__ - - assert isinstance(User.__dict__["get_stuff"], classmethod) - assert_equal("User", User.get_stuff()) - flexmock(User).should_receive("get_stuff").and_return("foo") - assert_equal("foo", User.get_stuff()) - self._tear_down() - assert_equal("User", User.get_stuff()) - assert isinstance(User.__dict__["get_stuff"], classmethod) - - def test_spy_should_match_return_value_class(self): - class User: - pass - - user = User() - foo = flexmock( - foo=lambda: ("bar", "baz"), - bar=lambda: user, - baz=lambda: None, - bax=lambda: None, - ) - foo.should_call("foo").and_return(str, str) - foo.should_call("bar").and_return(User) - foo.should_call("baz").and_return(object) - foo.should_call("bax").and_return(None) - assert_equal(("bar", "baz"), foo.foo()) - assert_equal(user, foo.bar()) - assert_equal(None, foo.baz()) - assert_equal(None, foo.bax()) - - def test_spy_should_not_match_falsy_stuff(self): - class Foo: - def foo(self): - return None - - def bar(self): - return False - - def baz(self): - return [] - - def quux(self): - return "" - - foo = Foo() - flexmock(foo).should_call("foo").and_return("bar").once() - flexmock(foo).should_call("bar").and_return("bar").once() - flexmock(foo).should_call("baz").and_return("bar").once() - flexmock(foo).should_call("quux").and_return("bar").once() - with assert_raises( - FlexmockError, - ( - "Returned values for call foo did not match expectation:\n" - " Expected:\t'bar'\n" - " Returned:\tNone" - ), - ): - foo.foo() - with assert_raises( - FlexmockError, - ( - "Returned values for call bar did not match expectation:\n" - " Expected:\t'bar'\n" - " Returned:\tFalse" - ), - ): - foo.bar() - with assert_raises( - FlexmockError, - ( - "Returned values for call baz did not match expectation:\n" - " Expected:\t'bar'\n" - " Returned:\t[]" - ), - ): - foo.baz() - with assert_raises( - FlexmockError, - ( - "Returned values for call quux did not match expectation:\n" - " Expected:\t'bar'\n" - " Returned:\t''" - ), - ): - foo.quux() - - def test_new_instances_should_blow_up_on_should_receive(self): - class User: - pass - - mock = flexmock(User).new_instances(None).mock() - with assert_raises(FlexmockError, "User does not have attribute 'foo'"): - mock.should_receive("foo") - - def test_should_call_alias_should_create_a_spy(self): - class Foo: - def get_stuff(self): - return "yay" - - foo = Foo() - flexmock(foo).should_call("get_stuff").and_return("yay").once() - with assert_raises( - MethodCallError, "get_stuff() expected to be called exactly 1 time, called 0 times" - ): - self._tear_down() - - def test_flexmock_should_fail_mocking_nonexistent_methods(self): - class User: - pass - - user = User() - with assert_raises( - FlexmockError, - re.compile( - r"<.+\.\.User object at 0x.+> does not have attribute 'nonexistent'" - ), - ): - flexmock(user).should_receive("nonexistent") - - def test_flexmock_should_not_explode_on_unicode_formatting(self): - formatted = _format_args("method", {"kargs": (chr(0x86C7),), "kwargs": {}}) - assert_equal('method("蛇")', formatted) - - def test_return_value_should_not_explode_on_unicode_values(self): - return_value = ReturnValue(chr(0x86C7)) - assert_equal('"蛇"', f"{return_value}") - return_value = ReturnValue((chr(0x86C7), chr(0x86C7))) - assert_equal('("蛇", "蛇")', f"{return_value}") - - def test_pass_thru_should_call_original_method_only_once(self): - class Nyan: - def __init__(self): - self.n = 0 - - def method(self): - self.n += 1 - - obj = Nyan() - flexmock(obj) - obj.should_call("method") - obj.method() - assert_equal(obj.n, 1) - - def test_should_call_works_for_same_method_with_different_args(self): - class Foo: - def method(self, arg): - pass - - foo = Foo() - flexmock(foo).should_call("method").with_args("foo").once() - flexmock(foo).should_call("method").with_args("bar").once() - foo.method("foo") - foo.method("bar") - self._tear_down() - - def test_should_call_fails_properly_for_same_method_with_different_args(self): - class Foo: - def method(self, arg): - pass - - foo = Foo() - flexmock(foo).should_call("method").with_args("foo").once() - flexmock(foo).should_call("method").with_args("bar").once() - foo.method("foo") - with assert_raises( - MethodCallError, - 'method(arg="bar") expected to be called exactly 1 time, called 0 times', - ): - self._tear_down() - - def test_should_give_reasonable_error_for_builtins(self): - with assert_raises( - MockBuiltinError, - ( - "Python does not allow you to mock builtin objects or modules. " - "Consider wrapping it in a class you can mock instead" - ), - ): - flexmock(object) - - def test_should_give_reasonable_error_for_instances_of_builtins(self): - with assert_raises( - MockBuiltinError, - ( - "Python does not allow you to mock instances of builtin objects. " - "Consider wrapping it in a class you can mock instead" - ), - ): - flexmock(object()) - - def test_mock_chained_method_calls_works_with_one_level(self): - class Foo: - def method2(self): - return "foo" - - class Bar: - def method1(self): - return Foo() - - foo = Bar() - assert_equal("foo", foo.method1().method2()) - flexmock(foo).should_receive("method1.method2").and_return("bar") - assert_equal("bar", foo.method1().method2()) - - def test_mock_chained_method_supports_args_and_mocks(self): - class Foo: - def method2(self, arg): - return arg - - class Bar: - def method1(self): - return Foo() - - foo = Bar() - assert_equal("foo", foo.method1().method2("foo")) - flexmock(foo).should_receive("method1.method2").with_args("foo").and_return("bar").once() - assert_equal("bar", foo.method1().method2("foo")) - self._tear_down() - flexmock(foo).should_receive("method1.method2").with_args("foo").and_return("bar").once() - with assert_raises( - MethodCallError, 'method2("foo") expected to be called exactly 1 time, called 0 times' - ): - self._tear_down() - - def test_mock_chained_method_calls_works_with_more_than_one_level(self): - class Baz: - def method3(self): - return "foo" - - class Foo: - def method2(self): - return Baz() - - class Bar: - def method1(self): - return Foo() - - foo = Bar() - assert_equal("foo", foo.method1().method2().method3()) - flexmock(foo).should_receive("method1.method2.method3").and_return("bar") - assert_equal("bar", foo.method1().method2().method3()) - - def test_flexmock_should_replace_method(self): - class Foo: - def method(self, arg): - return arg - - foo = Foo() - flexmock(foo).should_receive("method").replace_with(lambda x: x == 5) - assert_equal(foo.method(5), True) - assert_equal(foo.method(4), False) - - def test_flexmock_should_replace_cannot_be_specified_twice(self): - class Foo: - def method(self, arg): - return arg - - foo = Foo() - expectation = flexmock(foo).should_receive("method").replace_with(lambda x: x == 5) - with assert_raises(FlexmockError, "replace_with cannot be specified twice"): - expectation.replace_with(lambda x: x == 3) - - def test_flexmock_should_mock_the_same_method_multiple_times(self): - class Foo: - def method(self): - pass - - foo = Foo() - flexmock(foo).should_receive("method").and_return(1) - assert_equal(foo.method(), 1) - flexmock(foo).should_receive("method").and_return(2) - assert_equal(foo.method(), 2) - flexmock(foo).should_receive("method").and_return(3) - assert_equal(foo.method(), 3) - flexmock(foo).should_receive("method").and_return(4) - assert_equal(foo.method(), 4) - - def test_new_instances_should_be_a_method(self): - class Foo: - pass - - flexmock(Foo).new_instances("bar") - assert_equal("bar", Foo()) - self._tear_down() - assert Foo() != "bar" - - def test_new_instances_raises_error_when_not_a_class(self): - class Foo: - pass - - foo = Foo() - flexmock(foo) - with assert_raises(FlexmockError, "new_instances can only be called on a class mock"): - foo.new_instances("bar") - - def test_new_instances_works_with_multiple_return_values(self): - class Foo: - pass - - flexmock(Foo).new_instances("foo", "bar") - assert_equal("foo", Foo()) - assert_equal("bar", Foo()) - - def test_should_receive_should_not_replace_flexmock_methods(self): - class Foo: - def bar(self): - pass - - foo = Foo() - flexmock(foo) - for method_name in ["should_receive", "should_call", "new_instances"]: - with assert_raises(FlexmockError, "unable to replace flexmock methods"): - foo.should_receive(method_name) - - def test_flexmock_should_not_add_methods_if_they_already_exist(self): - class Foo: - def should_receive(self): - return "real" - - def bar(self): - pass - - foo = Foo() - mock = flexmock(foo) - assert_equal(foo.should_receive(), "real") - assert "should_call" not in dir(foo) - assert "new_instances" not in dir(foo) - mock.should_receive("bar").and_return("baz") - assert_equal(foo.bar(), "baz") - self._tear_down() - assert_equal(foo.should_receive(), "real") - - def test_flexmock_should_not_add_class_methods_if_they_already_exist(self): - class Foo: - def should_receive(self): - return "real" - - def bar(self): - pass - - foo = Foo() - mock = flexmock(Foo) - assert_equal(foo.should_receive(), "real") - assert "should_call" not in dir(Foo) - assert "new_instances" not in dir(Foo) - mock.should_receive("bar").and_return("baz") - assert_equal(foo.bar(), "baz") - self._tear_down() - assert_equal(foo.should_receive(), "real") - - def test_expectation_properties_work_with_parens(self): - foo = flexmock().should_receive("bar").at_least().once().and_return("baz").mock() - assert_equal("baz", foo.bar()) - - def test_mocking_down_the_inheritance_chain_class_to_class(self): - class Parent: - def foo(self): - pass - - class Child(Parent): - def bar(self): - pass - - flexmock(Parent).should_receive("foo").and_return("outer") - flexmock(Child).should_receive("bar").and_return("inner") - assert Parent().foo() == "outer" - assert Child().bar() == "inner" - - def test_arg_matching_works_with_regexp(self): - class Foo: - def foo(self, arg1, arg2): - pass - - foo = Foo() - flexmock(foo).should_receive("foo").with_args( - re.compile("^arg1.*asdf$"), arg2=re.compile("f") - ).and_return("mocked") - assert_equal("mocked", foo.foo("arg1somejunkasdf", arg2="aadsfdas")) - - def test_arg_matching_with_regexp_fails_when_regexp_doesnt_match_karg(self): - class Foo: - def foo(self, arg1, arg2): - pass - - foo = Foo() - flexmock(foo).should_receive("foo").with_args( - re.compile("^arg1.*asdf$"), arg2=re.compile("a") - ).and_return("mocked") - with assert_raises( - MethodSignatureError, - ( - "Arguments for call foo did not match expectations:\n" - ' Received call:\tfoo("arg1somejunkasdfa", arg2="a")\n' - " Expected call[1]:\tfoo(arg2=/a/, arg1=/^arg1.*asdf$/)" - ), - ): - foo.foo("arg1somejunkasdfa", arg2="a") - - def test_arg_matching_with_regexp_fails_when_regexp_doesnt_match_kwarg(self): - class Foo: - def foo(self, arg1, arg2): - pass - - foo = Foo() - flexmock(foo).should_receive("foo").with_args( - re.compile("^arg1.*asdf$"), arg2=re.compile("a") - ).and_return("mocked") - with assert_raises( - MethodSignatureError, - ( - "Arguments for call foo did not match expectations:\n" - ' Received call:\tfoo("arg1somejunkasdf", arg2="b")\n' - " Expected call[1]:\tfoo(arg2=/a/, arg1=/^arg1.*asdf$/)" - ), - ): - foo.foo("arg1somejunkasdf", arg2="b") - - def test_flexmock_class_returns_same_object_on_repeated_calls(self): - class Foo: - pass - - a = flexmock(Foo) - b = flexmock(Foo) - assert_equal(a, b) - - def test_flexmock_object_returns_same_object_on_repeated_calls(self): - class Foo: - pass - - foo = Foo() - a = flexmock(foo) - b = flexmock(foo) - assert_equal(a, b) - - def test_flexmock_ordered_worked_after_default_stub(self): - foo = flexmock() - foo.should_receive("bar") - foo.should_receive("bar").with_args("a").ordered() - foo.should_receive("bar").with_args("b").ordered() - with assert_raises(CallOrderError, 'bar("b") called before bar("a")'): - foo.bar("b") - - def test_flexmock_ordered_works_with_same_args(self): - foo = flexmock() - foo.should_receive("bar").ordered().and_return(1) - foo.should_receive("bar").ordered().and_return(2) - a = foo.bar() - assert_equal(a, 1) - b = foo.bar() - assert_equal(b, 2) - - def test_flexmock_ordered_works_with_same_args_after_default_stub(self): - foo = flexmock() - foo.should_receive("bar").and_return(9) - foo.should_receive("bar").ordered().and_return(1) - foo.should_receive("bar").ordered().and_return(2) - a = foo.bar() - assert_equal(a, 1) - b = foo.bar() - assert_equal(b, 2) - c = foo.bar() - assert_equal(c, 9) - - def test_state_machine(self): - class Radio: - def __init__(self): - self.is_on = False - self.volume = 0 - - def switch_on(self): - self.is_on = True - - def switch_off(self): - self.is_on = False - - def select_channel(self): - return None - - def adjust_volume(self, num): - self.volume = num - - radio = Radio() - flexmock(radio) - - def radio_is_on(): - return radio.is_on - - radio.should_receive("select_channel").once().when(lambda: radio.is_on) - radio.should_call("adjust_volume").once().with_args(5).when(radio_is_on) - - with assert_raises( - StateError, "select_channel expected to be called when lambda: radio.is_on is True" - ): - radio.select_channel() - with assert_raises( - StateError, "adjust_volume expected to be called when radio_is_on is True" - ): - radio.adjust_volume(5) - radio.is_on = True - radio.select_channel() - radio.adjust_volume(5) - - def test_when_parameter_should_be_callable(self): - with assert_raises(FlexmockError, "when() parameter must be callable"): - flexmock().should_receive("something").when(1) - - def test_flexmock_should_not_blow_up_with_builtin_in_when(self): - # It is not possible to get source for builtins. Flexmock should handle - # this gracefully. - mock = flexmock() - mock.should_receive("something").when(functools.partial(lambda: False)) - with assert_raises(StateError, "something expected to be called when condition is True"): - # Should not raise TypeError - mock.something() - - def test_support_at_least_and_at_most_together(self): - class Foo: - def bar(self): - pass - - foo = Foo() - flexmock(foo).should_call("bar").at_least().once().at_most().twice() - with assert_raises( - MethodCallError, - "bar() expected to be called at least 1 time and at most 2 times, called 0 times", - ): - self._tear_down() - - flexmock(foo).should_call("bar").at_least().once().at_most().twice() - foo.bar() - foo.bar() - with assert_raises( - MethodCallError, "bar() expected to be called at most 2 times, called 3 times" - ): - foo.bar() - - flexmock(foo).should_call("bar").at_least().once().at_most().twice() - foo.bar() - self._tear_down() - - flexmock(foo).should_call("bar").at_least().once().at_most().twice() - foo.bar() - foo.bar() - self._tear_down() - - def test_at_least_cannot_be_used_twice(self): - class Foo: - def bar(self): - pass - - expectation = flexmock(Foo).should_receive("bar") - with assert_raises(FlexmockError, "cannot use at_least modifier twice"): - expectation.at_least().at_least() - - def test_at_most_cannot_be_used_twice(self): - class Foo: - def bar(self): - pass - - expectation = flexmock(Foo).should_receive("bar") - with assert_raises(FlexmockError, "cannot use at_most modifier twice"): - expectation.at_most().at_most() - - def test_at_least_cannot_be_specified_until_at_most_is_set(self): - class Foo: - def bar(self): - pass - - expectation = flexmock(Foo).should_receive("bar") - with assert_raises(FlexmockError, "cannot use at_most with at_least unset"): - expectation.at_least().at_most() - - def test_at_most_cannot_be_specified_until_at_least_is_set(self): - class Foo: - def bar(self): - pass - - expectation = flexmock(Foo).should_receive("bar") - with assert_raises(FlexmockError, "cannot use at_least with at_most unset"): - expectation.at_most().at_least() - - def test_proper_reset_of_subclass_methods(self): - class A: - def x(self): - return "a" - - class B(A): - def x(self): - return "b" - - flexmock(B).should_receive("x").and_return("1") - self._tear_down() - assert_equal("b", B().x()) - - def test_format_args_supports_tuples(self): - formatted = _format_args("method", {"kargs": ((1, 2),), "kwargs": {}}) - assert_equal("method((1, 2))", formatted) - - def test_mocking_subclass_of_str(self): - class String(str): - pass - - s = String() - flexmock(s, endswith="fake") - assert_equal("fake", s.endswith("stuff")) - self._tear_down() - assert_equal(False, s.endswith("stuff")) - - def test_ordered_on_different_methods(self): - class String(str): - pass - - s = String("abc") - flexmock(s) - s.should_call("startswith").with_args("asdf", 0, 4).ordered() - s.should_call("endswith").ordered() - with assert_raises( - CallOrderError, - re.compile( - r'endswith\("c"\) called before startswith' - # Argument names are displayed in PyPy - r'\((prefix=)?"asdf", (start=)?0, (end=)?4\)' - ), - ): - s.endswith("c") - - def test_fake_object_takes_properties(self): - foo = flexmock(bar=property(lambda self: "baz")) - bar = flexmock(foo=property(lambda self: "baz")) - assert_equal("baz", foo.bar) - assert_equal("baz", bar.foo) - - def test_replace_non_callable_class_attributes(self): - class Foo: - bar = 1 - - foo = Foo() - bar = Foo() - flexmock(foo, bar=2) - assert_equal(2, foo.bar) - assert_equal(1, bar.bar) - self._tear_down() - assert_equal(1, foo.bar) - - def test_should_chain_attributes(self): - class Baz: - x = 1 - - class Bar: - baz = Baz() - - class Foo: - bar = Bar() - - foo = Foo() - foo = flexmock(foo) - foo.should_receive("bar.baz.x").and_return(2) - assert_equal(2, foo.bar.baz.x) - self._tear_down() - assert_equal(1, foo.bar.baz.x) - - def test_replace_non_callable_instance_attributes(self): - class Foo: - def __init__(self): - self.bar = 1 - - foo = Foo() - bar = Foo() - flexmock(foo, bar=2) - flexmock(bar, bar=1) - assert_equal(2, foo.bar) - self._tear_down() - assert_equal(1, foo.bar) - - def test_replace_non_callable_module_attributes(self): - if "tests.test_flexmock" in sys.modules: - mod = sys.modules["tests.test_flexmock"] - else: - mod = sys.modules["__main__"] - flexmock(mod, MODULE_LEVEL_ATTRIBUTE="yay") - assert_equal("yay", MODULE_LEVEL_ATTRIBUTE) - self._tear_down() - assert_equal("test", MODULE_LEVEL_ATTRIBUTE) - - def test_non_callable_attributes_fail_to_set_expectations(self): - class Foo: - bar = 1 - - foo = Foo() - e = flexmock(foo).should_receive("bar").and_return(2) - with assert_raises(FlexmockError, "can't use times() with attribute stubs"): - e.times(1) - with assert_raises(FlexmockError, "can't use with_args() with attribute stubs"): - e.with_args(()) - with assert_raises(FlexmockError, "can't use replace_with() with attribute/property stubs"): - e.replace_with(lambda x: x) - with assert_raises(FlexmockError, "can't use and_raise() with attribute stubs"): - e.and_raise(Exception) - with assert_raises(FlexmockError, "can't use when() with attribute stubs"): - e.when(lambda x: x) - with assert_raises(FlexmockError, "can't use and_yield() with attribute stubs"): - e.and_yield(1) - with assert_raises(FlexmockError, "can't use ordered() with attribute stubs"): - object.__getattribute__(e, "ordered")() - with assert_raises(FlexmockError, "can't use at_least() with attribute stubs"): - object.__getattribute__(e, "at_least")() - with assert_raises(FlexmockError, "can't use at_most() with attribute stubs"): - object.__getattribute__(e, "at_most")() - with assert_raises(FlexmockError, "can't use one_by_one() with attribute stubs"): - object.__getattribute__(e, "one_by_one")() - - def test_and_return_defaults_to_none_with_no_arguments(self): - foo = flexmock() - foo.should_receive("bar").and_return() - assert_equal(None, foo.bar()) - - def test_should_replace_attributes_that_are_instances_of_classes(self): - class Foo: - pass - - class Bar: - foo = Foo() - - bar = Bar() - flexmock(bar, foo="test") - assert_equal("test", bar.foo) - - def test_isproperty(self): - class Foo: - @property - def bar(self): - pass - - def baz(self): - pass - - class Bar(Foo): - pass - - foo = Foo() - bar = Bar() - assert_equal(True, _isproperty(foo, "bar")) - assert_equal(False, _isproperty(foo, "baz")) - assert_equal(True, _isproperty(Foo, "bar")) - assert_equal(False, _isproperty(Foo, "baz")) - assert_equal(True, _isproperty(bar, "bar")) - assert_equal(False, _isproperty(bar, "baz")) - assert_equal(True, _isproperty(Bar, "bar")) - assert_equal(False, _isproperty(Bar, "baz")) - assert_equal(False, _isproperty(Mock(), "baz")) - - def test_fake_object_supporting_iteration(self): - foo = flexmock() - foo.should_receive("__iter__").and_yield(1, 2, 3) - assert_equal([1, 2, 3], list(foo)) - - def test_with_args_for_single_named_arg_with_optional_args(self): - class Foo: - def bar(self, one, two="optional"): - pass - - e = flexmock(Foo).should_receive("bar") - e.with_args(one=1) - - def test_with_args_doesnt_set_max_when_using_varargs(self): - class Foo: - def bar(self, *kargs): - pass - - flexmock(Foo).should_receive("bar").with_args(1, 2, 3) - - def test_with_args_doesnt_set_max_when_using_kwargs(self): - class Foo: - def bar(self, **kwargs): - pass - - flexmock(Foo).should_receive("bar").with_args(1, 2, 3) - - def test_with_args_blows_up_on_too_few_args(self): - class Foo: - def foo(self, a): - pass - - def bar(self, a, b, c=1): - pass - - mock = flexmock(Foo) - e1 = mock.should_receive("foo") - e2 = mock.should_receive("bar") - with assert_raises( - MethodSignatureError, "foo requires at least 1 argument, expectation provided 0" - ): - e1.with_args() - with assert_raises( - MethodSignatureError, "bar requires at least 2 arguments, expectation provided 1" - ): - e2.with_args(1) - - def test_with_args_blows_up_on_too_few_args_with_kwargs(self): - class Foo: - def bar(self, a, b, c=1): - pass - - e = flexmock(Foo).should_receive("bar") - with assert_raises( - MethodSignatureError, "bar requires at least 3 arguments, expectation provided 2" - ): - e.with_args(1, c=2) - - def test_with_args_blows_up_on_too_many_args(self): - class Foo: - def bar(self, a, b, c=1): - pass - - e = flexmock(Foo).should_receive("bar") - with assert_raises( - MethodSignatureError, "bar requires at most 3 arguments, expectation provided 4" - ): - e.with_args(1, 2, 3, 4) - - def test_with_args_blows_up_on_kwarg_overlapping_positional(self): - class Foo: - def bar(self, a, b, c=1, **kwargs): - pass - - e = flexmock(Foo).should_receive("bar") - with assert_raises( - MethodSignatureError, "['c'] already given as positional argument to bar" - ): - e.with_args(1, 2, 3, c=2) - with assert_raises( - MethodSignatureError, "['c', 'b'] already given as positional arguments to bar" - ): - e.with_args(1, 2, 3, c=2, b=3) - - def test_with_args_blows_up_on_invalid_kwarg(self): - class Foo: - def bar(self, a, b, c=1): - pass - - e = flexmock(Foo).should_receive("bar") - with assert_raises(MethodSignatureError, "d is not a valid keyword argument to bar"): - e.with_args(1, 2, d=2) - - def test_with_args_ignores_invalid_args_on_flexmock_instances(self): - foo = flexmock(bar=lambda x: x) - foo.should_receive("bar").with_args("stuff") - foo.bar("stuff") - - def test_with_args_does_not_compensate_for_self_on_static_instance_methods(self): - class Foo: - @staticmethod - def bar(x): - pass - - foo = Foo() - flexmock(foo).should_receive("bar").with_args("stuff") - foo.bar("stuff") - - def test_with_args_does_not_compensate_for_self_on_static_class_methods(self): - class Foo: - @staticmethod - def bar(x): - pass - - flexmock(Foo).should_receive("bar").with_args("stuff") - Foo.bar("stuff") - - def test_with_args_does_compensate_for_cls_on_class_methods(self): - class Foo: - @classmethod - def bar(cls, x): - pass - - foo = Foo() - flexmock(foo).should_receive("bar").with_args("stuff") - foo.bar("stuff") - - def test_calling_with_keyword_args_matches_mock_with_positional_args(self): - class Foo: - def bar(self, a, b, c): - pass - - foo = Foo() - flexmock(foo).should_receive("bar").with_args(1, 2, 3).once() - foo.bar(a=1, b=2, c=3) - - def test_calling_with_positional_args_matches_mock_with_kwargs(self): - class Foo: - def bar(self, a, b, c): - pass - - foo = Foo() - flexmock(foo).should_receive("bar").with_args(a=1, b=2, c=3).once() - foo.bar(1, 2, c=3) - - def test_use_replace_with_for_callable_shortcut_kwargs(self): - class Foo: - def bar(self): - return "bar" - - foo = Foo() - flexmock(foo, bar=lambda: "baz") - assert_equal("baz", foo.bar()) - - def test_mock_multiple_properties(self): - class Foo: - @property - def prop1(self): - return "prop1" - - @property - def prop2(self): - return "prop2" - - mocked = Foo() - flexmock(mocked, prop1="mocked1", prop2="mocked2") - assert_equal("mocked1", mocked.prop1) - assert_equal("mocked2", mocked.prop2) - - def test_mock_property_with_attribute_on_instance(self): - class Foo: - @property - def bar(self): - return "bar" - - foo = Foo() - foo2 = Foo() - foo3 = Foo() - flexmock(foo, bar="baz") - flexmock(foo2, bar="baz2") - assert_equal("baz", foo.bar) - assert_equal("baz2", foo2.bar) - assert_equal("bar", foo3.bar) - self._tear_down() - assert_equal(False, hasattr(Foo, "_flexmock__bar"), "Property bar not cleaned up") - assert_equal("bar", foo.bar) - assert_equal("bar", foo2.bar) - assert_equal("bar", foo3.bar) - - def test_mock_property_with_attribute_on_class(self): - class Foo: - @property - def bar(self): - return "bar" - - foo = Foo() - foo2 = Foo() - flexmock(Foo, bar="baz") - assert_equal("baz", foo.bar) - assert_equal("baz", foo2.bar) - self._tear_down() - assert_equal(False, hasattr(Foo, "_flexmock__bar"), "Property bar not cleaned up") - assert_equal("bar", foo.bar) - assert_equal("bar", foo2.bar) - - def test_verifying_methods_when_mocking_module(self): - # previously, we had problems with recognizing methods vs functions if the mocked - # object was an imported module - flexmock(some_module).should_receive("ModuleClass").with_args(1, 2) - flexmock(some_module).should_receive("module_function").with_args(1, 2) - - def test_is_class_method(self): - assert _is_class_method(SomeClass.class_method, "class_method") is True - assert _is_class_method(SomeClass.static_method, "static_method") is False - assert _is_class_method(SomeClass.instance_method, "instance_method") is False - # Method names do no match - assert _is_class_method(SomeClass.class_method, "other_method") is False - - some_class = SomeClass() - assert _is_class_method(some_class.class_method, "class_method") is True - assert _is_class_method(some_class.static_method, "static_method") is False - assert _is_class_method(some_class.instance_method, "instance_method") is False - - assert _is_class_method(DerivedClass.class_method, "class_method") is True - assert _is_class_method(DerivedClass.static_method, "static_method") is False - assert _is_class_method(DerivedClass.instance_method, "instance_method") is False - - derived_class = DerivedClass() - assert _is_class_method(derived_class.class_method, "class_method") is True - assert _is_class_method(derived_class.static_method, "static_method") is False - assert _is_class_method(derived_class.instance_method, "instance_method") is False - - def test_is_class_method_proxied(self): - # pylint: disable=not-callable - SomeClassProxy = Proxy(SomeClass) - assert _is_class_method(SomeClassProxy.class_method, "class_method") is True - assert _is_class_method(SomeClassProxy.static_method, "static_method") is False - assert _is_class_method(SomeClassProxy.instance_method, "instance_method") is False - - some_class = SomeClassProxy() - assert _is_class_method(some_class.class_method, "class_method") is True - assert _is_class_method(some_class.static_method, "static_method") is False - assert _is_class_method(some_class.instance_method, "instance_method") is False - - DerivedClassProxy = Proxy(DerivedClass) - assert _is_class_method(DerivedClassProxy.class_method, "class_method") is True - assert _is_class_method(DerivedClassProxy.static_method, "static_method") is False - assert _is_class_method(DerivedClassProxy.instance_method, "instance_method") is False - - derived_class = DerivedClassProxy() - assert _is_class_method(derived_class.class_method, "class_method") is True - assert _is_class_method(derived_class.static_method, "static_method") is False - assert _is_class_method(derived_class.instance_method, "instance_method") is False - - def test_is_static_method(self): - assert _is_static_method(SomeClass, "class_method") is False - assert _is_static_method(SomeClass, "static_method") is True - assert _is_static_method(SomeClass, "instance_method") is False - - some_class = SomeClass() - assert _is_static_method(some_class, "class_method") is False - assert _is_static_method(some_class, "static_method") is True - assert _is_static_method(some_class, "instance_method") is False - - assert _is_static_method(DerivedClass, "class_method") is False - assert _is_static_method(DerivedClass, "static_method") is True - assert _is_static_method(DerivedClass, "instance_method") is False - - derived_class = DerivedClass() - assert _is_static_method(derived_class, "class_method") is False - assert _is_static_method(derived_class, "static_method") is True - assert _is_static_method(derived_class, "instance_method") is False - - def test_is_static_method_proxied(self): - # pylint: disable=not-callable - SomeClassProxy = Proxy(SomeClass) - assert _is_static_method(SomeClassProxy, "class_method") is False - assert _is_static_method(SomeClassProxy, "static_method") is True - assert _is_static_method(SomeClassProxy, "instance_method") is False - - some_class = SomeClassProxy() - assert _is_static_method(some_class, "class_method") is False - assert _is_static_method(some_class, "static_method") is True - assert _is_static_method(some_class, "instance_method") is False - - DerivedClassProxy = Proxy(DerivedClass) - assert _is_static_method(DerivedClassProxy, "class_method") is False - assert _is_static_method(DerivedClassProxy, "static_method") is True - assert _is_static_method(DerivedClassProxy, "instance_method") is False - - derived_class = DerivedClassProxy() - assert _is_static_method(derived_class, "class_method") is False - assert _is_static_method(derived_class, "static_method") is True - assert _is_static_method(derived_class, "instance_method") is False - - -class TestFlexmockUnittest(RegularClass, unittest.TestCase): - def tearDown(self): - pass - - def _tear_down(self): - return flexmock_teardown() - - -class ModernClass: - """Contains features only available in 2.6 and above.""" - - def test_context_manager_on_instance(self): - class CM: - def __enter__(self): - pass - - def __exit__(self, *_): - pass - - cm = CM() - flexmock(cm).should_call("__enter__").once() - flexmock(cm).should_call("__exit__").once() - with cm: - pass - self._tear_down() - - def test_context_manager_on_class(self): - class CM: - def __enter__(self): - pass - - def __exit__(self, *_): - pass - - cm = CM() - flexmock(CM).should_receive("__enter__").once() - flexmock(CM).should_receive("__exit__").once() - with cm: - pass - self._tear_down() - - def test_flexmock_should_support_with(self): - foo = flexmock() - with foo as mock: - mock.should_receive("bar").and_return("baz") - assert foo.bar() == "baz" - - def test_builtin_open(self): - mock = flexmock(sys.modules["builtins"]) - fake_fd = flexmock(read=lambda: "some data") - mock.should_receive("open").once().with_args("file_name").and_return(fake_fd) - with open("file_name") as f: # pylint: disable=unspecified-encoding - data = f.read() - self.assertEqual("some data", data) - - -class TestFlexmockUnittestModern(ModernClass, unittest.TestCase): - def _tear_down(self): - return unittest.TestCase.tearDown(self) - - -class TestUnittestModern(TestFlexmockUnittestModern): - pass - - -class TestPy3Features(unittest.TestCase): - def test_mock_kwargs_only_func_mock_all(self): - flexmock(some_module).should_receive("kwargs_only_func1").with_args( - 1, bar=2, baz=3 - ).and_return(123) - self.assertEqual(some_module.kwargs_only_func1(1, bar=2, baz=3), 123) - - def test_mock_kwargs_only_func_mock_required(self): - flexmock(some_module).should_receive("kwargs_only_func1").with_args(1, bar=2).and_return( - 123 - ) - self.assertEqual(some_module.kwargs_only_func1(1, bar=2), 123) - - def test_mock_kwargs_only_func_fails_if_required_not_provided(self): - with assert_raises( - MethodSignatureError, 'kwargs_only_func1 requires keyword-only argument "bar"' - ): - flexmock(some_module).should_receive("kwargs_only_func1").with_args(1) - with assert_raises( - MethodSignatureError, 'kwargs_only_func2 requires keyword-only arguments "bar", "baz"' - ): - flexmock(some_module).should_receive("kwargs_only_func2").with_args(2) - - -class TestReturnValue(unittest.TestCase): - def test_return_value_to_str(self): - r_val = ReturnValue(value=1) - assert str(r_val) == "1" - - r_val = ReturnValue(value=(1,)) - assert str(r_val) == "1" - - r_val = ReturnValue(value=(1, 2)) - assert str(r_val) == "(1, 2)" - - r_val = ReturnValue(value=1, raises=RuntimeError) - assert str(r_val) == "(1)" - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_integrations.py b/tests/test_integrations.py new file mode 100644 index 0000000..ceadfbd --- /dev/null +++ b/tests/test_integrations.py @@ -0,0 +1,9 @@ +"""Test integrations that work with unittest testcases.""" +# pylint: disable=missing-docstring,no-self-use +import unittest + +from tests.features import FlexmockTestCase + + +class TestIntegrations(FlexmockTestCase, unittest.TestCase): # pylint: disable=too-many-ancestors + pass diff --git a/tests/test_pytest.py b/tests/test_pytest.py index 450cee1..b787364 100644 --- a/tests/test_pytest.py +++ b/tests/test_pytest.py @@ -1,12 +1,14 @@ """Test flexmock with pytest.""" # pylint: disable=missing-docstring,redefined-outer-name,no-self-use +import unittest + import pytest from flexmock import flexmock from flexmock._api import flexmock_teardown from flexmock.exceptions import MethodCallError -from tests import test_flexmock -from tests.test_flexmock import assert_raises +from tests.features import FlexmockTestCase +from tests.utils import assert_raises def test_module_level_test_for_pytest(): @@ -37,7 +39,7 @@ def test_flexmock_teardown_works_with_pytest_part2(): mock.method2() -class TestForPytest(test_flexmock.RegularClass): +class TestForPytest(FlexmockTestCase): # pylint: disable=too-many-ancestors def test_class_level_test_for_pytest(self): flexmock(foo="bar").should_receive("foo").once() with assert_raises( @@ -46,7 +48,7 @@ def test_class_level_test_for_pytest(self): flexmock_teardown() -class TestUnittestClass(test_flexmock.TestFlexmockUnittest): +class TestUnittestClass(unittest.TestCase): def test_unittest(self): mocked = flexmock(a=2) mocked.should_receive("a").once() diff --git a/tests/test_teamcity.py b/tests/test_teamcity.py index e4c4a68..62f6fc1 100644 --- a/tests/test_teamcity.py +++ b/tests/test_teamcity.py @@ -4,21 +4,14 @@ from teamcity.unittestpy import TeamcityTestRunner -from flexmock import flexmock +from tests.features import FlexmockTestCase -class TestTeamCityTeardown(unittest.TestCase): +class TestTeamCityTeardown( # pylint: disable=too-many-ancestors + FlexmockTestCase, unittest.TestCase +): """Test flexmock teardown works with TeamCity test runner (PyCharm).""" - def test_flexmock_teardown_works_with_pytest_part1(self): - flexmock().should_receive("method1").ordered() - - def test_flexmock_teardown_works_with_pytest_part2(self): - mock = flexmock().should_receive("method2").ordered().mock() - # Raises CallOrderError if flexmock teardown is not automatically called - # after test part 1 above - mock.method2() - if __name__ == "__main__": runner = TeamcityTestRunner() diff --git a/tests/test_testtools.py b/tests/test_testtools.py index 9fc1b57..05ea3c2 100644 --- a/tests/test_testtools.py +++ b/tests/test_testtools.py @@ -1,47 +1,11 @@ """Test testtools integration.""" # pylint: disable=missing-docstring,no-self-use -from contextlib import suppress +import testtools -from testtools import TestCase -from testtools.matchers import Equals +from tests.features import FlexmockTestCase -from flexmock import exceptions, flexmock -from flexmock._api import flexmock_teardown - -class SomeClass: - def __init__(self): - self.value = "some_value" - - def method(self): - return self.value - - -class TestFlexmock(TestCase): - def test_mocking_works(self): - - flexmock(SomeClass).should_receive("method").and_return("mocked_value").once() - self.assertThat(SomeClass().method(), Equals("mocked_value")) - flexmock_teardown() - self.assertThat(SomeClass().method(), Equals("some_value")) - - def test_spying_works(self): - flexmock(SomeClass).should_call("method").and_return("some_value").once() - self.assertThat(SomeClass().method(), Equals("some_value")) - flexmock_teardown() - self.assertThat(SomeClass().method(), Equals("some_value")) - - def test_exception_is_raised(self): - mocked = flexmock().should_receive("method").and_return(1).twice().mock() - self.assertThat(mocked.method(), Equals(1)) - with suppress(exceptions.MethodCallError): - flexmock_teardown() - - def test_flexmock_teardown_works_with_testtools_part1(self): - flexmock().should_receive("method1").ordered() - - def test_flexmock_teardown_works_with_testtools_part2(self): - mock = flexmock().should_receive("method2").ordered().mock() - # Raises CallOrderError if flexmock teardown is not automatically called - # after test part 1 above - mock.method2() +class TestTestoolsIntegration( + FlexmockTestCase, testtools.TestCase +): # pylint: disable=too-many-ancestors + pass diff --git a/tests/test_unittest.py b/tests/test_unittest.py index 9907542..52efb3a 100644 --- a/tests/test_unittest.py +++ b/tests/test_unittest.py @@ -4,9 +4,12 @@ import unittest from flexmock import flexmock +from tests.features import FlexmockTestCase -class TestUnitTestIntegration(unittest.TestCase): +class TestUnitTestIntegration( + FlexmockTestCase, unittest.TestCase +): # pylint: disable=too-many-ancestors """Flexmock unittest integration specific tests.""" def test_failed_test_case(self): @@ -18,9 +21,8 @@ def test_failed_test_case(self): if __name__ == "__main__": EXPECTED_FAILURES = 1 - EXPECTED_TESTS = 1 test = unittest.main(exit=False) - if len(test.result.failures) == EXPECTED_FAILURES and test.result.testsRun == EXPECTED_TESTS: + if len(test.result.failures) == EXPECTED_FAILURES and test.result.testsRun > EXPECTED_FAILURES: sys.exit(0) # OK sys.exit(1) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..fd601a8 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,38 @@ +"""Utility functions for testing.""" +from contextlib import contextmanager +from typing import Type, Union + +from flexmock._api import RE_TYPE + + +@contextmanager +def assert_raises(expected_exception: Type[BaseException], match: Union[RE_TYPE, str, None]): + """Assert that code raises the correct exception with a correct error message. + + Args: + expected_exception: Type of the expected exception. + match: String or pattern to match the error message against. Use None + to skip error message checking. + """ + try: + yield + except Exception as raised_exception: + if not isinstance(raised_exception, expected_exception): + raise AssertionError( + f"Expected exception '{type(expected_exception)}' " + f"but '{type(raised_exception)}' was raised" + ) from raised_exception + if match is not None: + fail = False + if isinstance(match, RE_TYPE): + fail = not match.search(str(raised_exception)) + match = match.pattern + else: + fail = str(raised_exception) != str(match) + if fail: + raise AssertionError( + f"Expected error message:\n{'-'*39}\n'{str(match)}'\n" + f"\nBut got:\n\n'{str(raised_exception)}'\n{'-'*39}\n" + ) from raised_exception + else: + raise AssertionError(f"Exception '{expected_exception.__name__}' not raised") diff --git a/tox.ini b/tox.ini index ff2c1a7..2290147 100644 --- a/tox.ini +++ b/tox.ini @@ -29,12 +29,11 @@ whitelist_externals= sh commands = - python -m unittest tests/test_flexmock.py python tests/test_unittest.py python tests/test_doctest.py - python -c "from twisted.scripts.trial import run; run();" tests/test_flexmock.py - zope-testrunner --test-path=./ --test-file-pattern=test_flexmock --verbose + python -c "from twisted.scripts.trial import run; run();" tests/test_integrations.py + zope-testrunner --test-path=./ --test-file-pattern=test_integrations --verbose python -m testtools.run --verbose tests/test_testtools.py - sh -c 'python -m subunit.run tests/test_flexmock.py | subunit2pyunit' + sh -c 'python -m subunit.run tests/test_integrations.py | subunit2pyunit' python tests/test_teamcity.py pytest tests/test_pytest.py