OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

Python unittest.mock, wrap instance method, turn mock on/off

  • Thread starter Thread starter Wör Du Schnaffzig
  • Start date Start date
W

Wör Du Schnaffzig

Guest
I would like to write an unit test for an instance method of a class under test containing some simple logic but calling another more complicated instance method.

I would like to wrap the complicated instance method in a mock. Additionally, it should be possible to forwards calls to the original method via the mock, when requested. If the call is forwarded, the spec of the original method should be used.

I would like to control that via the return value of the mock, if it is unittest.mock.DEFAULT pass the call through, if it is something else, mock out the entire method.

In the code, I came up with, I define a class under test (A) and a derived fake class (FakeA) which mocks out the complicated method. The derived class serves as an isolation layer so that dependencies of the test code to the concrete production code are centralized and kept at a minimum level.

One of the tests checks the logic of the simple_method and mocks out the complicated_method at all. The other test should call the complicated_method via the simple_method but mock out external dependencies.

The problem now is that the spec of the complicated_method is not determined correctly. Therefore I get a failing test1 with the error message:

Code:
        if self._mock_wraps is not None:
>           return self._mock_wraps(*args, **kwargs)
E           TypeError: A.complicated_method() missing 1 required positional argument: 'self'

It seems, the mock is errornously considered as a classmethod whereas the original method is an instancemethod.

How can I fix this?

Code:
import unittest.mock
from unittest.mock import MagicMock


class A:
    def simple_method(self):
        return self.complicated_method()

    def complicated_method(self):
        # Calls some external dependency
        return 42


class FakeA(A):
    def __init__(self):
        super().__init__()
        type(self).complicated_method = MagicMock(wraps=A.complicated_method, autospec=True,
                                                  return_value=unittest.mock.DEFAULT)

    """
    Besides mocking out nested calls, the Fake class should provide some additional methods wrapping internals of class A so    
    that the tests need only minimal adaption when the production code changes
    """

class TestClassA:
    def test1(self):
        # This test lets the call pass through to the original method
        # Therefor we assume here, that external dependencies have been mocked out
        a = FakeA()
        assert a.simple_method() == 42

    def test2(self):
        # This test mocks out the original method at all providing a fake return_value
        a = FakeA()
        a.complicated_method.return_value = 77
        assert a.simple_method() == 77

Here is my second attempt, which fails for the same reason:

Code:
class FakeA(MagicMock(wraps=A, autospec=A)):
    def __init__(self):
        super().__init__()
        type(self).complicated_method.return_value=unittest.mock.DEFAULT
<p>I would like to write an unit test for an instance method of a class under test containing some simple logic but calling another more complicated instance method.</p>
<p>I would like to wrap the complicated instance method in a mock.
Additionally, it should be possible to forwards calls to the original method via the mock, when requested. If the call is forwarded, the spec of the original method should be used.</p>
<p>I would like to control that via the return value of the mock, if it is <code>unittest.mock.DEFAULT</code> pass the call through, if it is something else, mock out the entire method.</p>
<p>In the code, I came up with, I define a class under test (<code>A</code>) and a derived fake class (<code>FakeA</code>) which mocks out the complicated method. The derived class serves as an isolation layer so that dependencies of the test code to the concrete production code are centralized and kept at a minimum level.</p>
<p>One of the tests checks the logic of the <code>simple_method</code> and mocks out the <code>complicated_method</code> at all. The other test should call the <code>complicated_method</code> via the <code>simple_method</code> but mock out external dependencies.</p>
<p>The problem now is that the spec of the <code>complicated_method</code> is not determined correctly.
Therefore I get a failing <code>test1</code> with the error message:</p>
<pre><code> if self._mock_wraps is not None:
> return self._mock_wraps(*args, **kwargs)
E TypeError: A.complicated_method() missing 1 required positional argument: 'self'
</code></pre>
<p>It seems, the mock is errornously considered as a <code>classmethod</code> whereas the original method is an <code>instancemethod</code>.</p>
<p>How can I fix this?</p>
<pre class="lang-py prettyprint-override"><code>import unittest.mock
from unittest.mock import MagicMock


class A:
def simple_method(self):
return self.complicated_method()

def complicated_method(self):
# Calls some external dependency
return 42


class FakeA(A):
def __init__(self):
super().__init__()
type(self).complicated_method = MagicMock(wraps=A.complicated_method, autospec=True,
return_value=unittest.mock.DEFAULT)

"""
Besides mocking out nested calls, the Fake class should provide some additional methods wrapping internals of class A so
that the tests need only minimal adaption when the production code changes
"""

class TestClassA:
def test1(self):
# This test lets the call pass through to the original method
# Therefor we assume here, that external dependencies have been mocked out
a = FakeA()
assert a.simple_method() == 42

def test2(self):
# This test mocks out the original method at all providing a fake return_value
a = FakeA()
a.complicated_method.return_value = 77
assert a.simple_method() == 77
</code></pre>
<p>Here is my second attempt, which fails for the same reason:</p>
<pre class="lang-py prettyprint-override"><code>class FakeA(MagicMock(wraps=A, autospec=A)):
def __init__(self):
super().__init__()
type(self).complicated_method.return_value=unittest.mock.DEFAULT
</code></pre>
 

Latest posts

A
Replies
0
Views
1
AgencyAnalytics
A
S
Replies
0
Views
1
Stacker Media
S
C
Replies
0
Views
1
CC.Talent
C
Top