Cara menggunakan mock variable python

app_client.py
-------------
class AppClient:
       
    def __init__(self):
        self.val = "true"
    
    def get_value(self, feature_eng):
        return self.val
....................................................

app_client_instance.py
----------------------
from app_client import AppClient

app_client_instance = AppClient()
....................................................

feat_eng.py
-----------
from app_client_instance import app_client_instance

class FeatEng:

    @staticmethod
    def is_activ(feature_eng):
        client = app_client_instance
        value = client.get_value(feature_eng)
        return "true" == value
    
....................................................
test.py
-------


import unittest
from unittest.mock import MagicMock
from app_client import AppClient
from app_client_instance import app_client_instance
from feat_eng import FeatEng

class FeatEngShould(unittest.TestCase):

    def test_mockmagshouldFalse(self):
        client = MagicMock(spec=AppClient, name=AppClient)
        client.get_value.return_value = "false"
        instance = MagicMock()
        instance.app_client_instance.return_value = client
        result = FeatEng.is_activ("MIG_ENG")
        self.assertFalse(result) 

Error:

Output:

$ pytest test.py ================================================================================== test session starts =================================================================================== platform darwin -- Python 3.8.2, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 rootdir: /Users/hocinema/projets/scripts/python plugins: hypothesis-5.43.3, anyio-2.0.2 collected 1 item test.py F [100%] ======================================================================================== FAILURES ======================================================================================== _______________________________________________________________________________ FeatEngShould.test_mockmag _______________________________________________________________________________ self = <test.FeatEngShould testMethod=test_mockmag> def test_mockmag(self): client = MagicMock(spec=AppClient, name=AppClient) client.get_value.return_value = "false" instance = MagicMock() instance.app_client_instance.return_value = client result = FeatEng.is_activ("MIG_ENG") > self.assertFalse(result) E AssertionError: True is not false test.py:16: AssertionError ================================================================================ short test summary info ================================================================================= FAILED test.py::FeatEngShould::test_mockmag - AssertionError: True is not false =================================================================================== 1 failed in 0.21s ====================================================================================
I want to make the mock client returning false to make success the test but instead it failed

Mocking in Python can be initially confusing and the official docs, while informative, don’t make learning it any easier for newcomers. In my case, I came across it when I was still transitioning from Ruby which, I believe, contributed to the confusion. This article takes a gentler approach to learning Python’s mocking library to help you get productive sooner.

Importing Objects: A Review

The unittest.mock library builds on top of how Python implements the import statement so it’s imperative that we have a solid understanding of how it works before we can continue.

Let’s say we have a file named

1
2
3
4
5
6
7
8
3 with the following code:

1
2
3
4
5
6
# module1.py

class A(object):

    def __init__(self):
      ...

And let’s also say we have a file named

1
2
3
4
5
6
7
8
4 with the following code:

1
2
3
4
5
6
7
8
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...

If you look at

1
2
3
4
5
6
7
8
4 line 3, what it’s effectively doing is that it’s creating a new variable named
1
2
3
4
5
6
7
8
6 that is local to
1
2
3
4
5
6
7
8
7 and this variable points to the actual
1
2
3
4
5
6
7
8
6 class in memory as defined by
1
2
3
4
5
6
7
8
9.

What this means is that we now have two variables pointing to the same memory address. These two variables are named

# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
0 and
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
1:

               |------------------|
module1.A ---> |                  |
               | <Actual A Class> |
module2.A ---> |                  |
               |------------------|

Now let’s say that, in

1
2
3
4
5
6
7
8
7, we used an unqualified
1
2
3
4
5
6
7
8
6 as in line 8 of the following:

1
2
3
4
5
6
7
8
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        self.a = A()

Behind the scenes, Python will try to find an

1
2
3
4
5
6
7
8
6 variable in the
1
2
3
4
5
6
7
8
7 namespace. After finding the variable, it then uses it to get to the actual
1
2
3
4
5
6
7
8
6 class in memory. Effectively, the
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
7 in line 8 above is shorthand for
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
8.

Mocking Objects

Building on top of what we learned in the previous section, let’s say we want to test our class

# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
9 from above. We would then write our test initially as follows:

1
2
3
4
5
6
7
8
# test_module2.py

from module2 import B

class TestB:

    def test_initialization(self):
        subject = B()

Now let’s say we want to focus our test on the logic of

# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
9 and not care about the internals of
1
2
3
4
5
6
7
8
6 for now. To do that, we need to mock out the variable
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
1 since that’s what all unqualified
1
2
3
4
5
6
7
8
6’s in
1
2
3
4
5
6
7
8
7 will resolve to.

To mock out all unqualified

1
2
3
4
5
6
7
8
6’s in
1
2
3
4
5
6
7
8
7, we use the patch decorator as in line 8 below:

1
2
3
4
5
6
7
8
9
10
# module1.py

class A(object):

    def __init__(self):
      ...
0

There’s a lot happening above so let’s break it down:

  1. Line 3:
                   |------------------|
    module1.A ---> |                  |
                   | <Actual A Class> |
    module2.A ---> |                  |
                   |------------------|
    
    7 makes the available to our tests.
  2. Line 8: Using the , we create an instance of and make the variable
    # module2.py
    
    from module1 import A
    
    class B(object):
    
        def __init__(self):
            ...
    
    1 point to this instance.

Reusing our diagram from above, our new reality is as follows:

# module1.py

class A(object):

    def __init__(self):
      ...
1

Our use of the

               |------------------|
module1.A ---> |                  |
               | <Actual A Class> |
module2.A ---> |                  |
               |------------------|
9 decorator above has another side effect. Namely that this decorator provides our test method with a reference to the Mock instance it created. We capture this reference using the
1
2
3
4
5
6
7
8
0 parameter in line 9.

So a more complete representation of our new reality is as follows:

# module1.py

class A(object):

    def __init__(self):
      ...
2

Note that since we are patching

# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
1 via the decorator method, this new reality is effective within the context of the
1
2
3
4
5
6
7
8
2 method only.

With that, we can now use

1
2
3
4
5
6
7
8
0 to describe how our mock
1
2
3
4
5
6
7
8
6 class should behave:

# module1.py

class A(object):

    def __init__(self):
      ...
3
# module1.py

class A(object):

    def __init__(self):
      ...
4

And that’s it! You now have the basics of mocking in Python. To dive deeper, visit .

Tips on Mocking Attributes and Methods

Stubbing Attributes

If you want to mock an attribute or property and you don’t care about how many times it was called (usually, you don’t), just stub it like so:

# module1.py

class A(object):

    def __init__(self):
      ...
5
# module1.py

class A(object):

    def __init__(self):
      ...
6

Mocking Instances

When the subject under test (SUT) attempts to instantiate an object from our Mock

1
2
3
4
5
6
7
8
6 class above, another Mock object is created and returned. This new Mock object pretends to be an instance of
1
2
3
4
5
6
7
8
6. If you want to customize how this mock instance of
1
2
3
4
5
6
7
8
6 behaves, first get a reference to it via the
1
2
3
4
5
6
7
8
8 attribute:

# module1.py

class A(object):

    def __init__(self):
      ...
7
# module1.py

class A(object):

    def __init__(self):
      ...
8

Returning Different Values

Now what if you want the mocked method to return “hello” on the first call and then “olleh!” in the second call (assuming the SUT calls it twice). You can mock it like so:

# module1.py

class A(object):

    def __init__(self):
      ...
9
1
2
3
4
5
6
7
8
0

Note how we’re using

1
2
3
4
5
6
7
8
9 in line 5 instead of
1
2
3
4
5
6
7
8
8. You can also assign a function to
1
2
3
4
5
6
7
8
9 but so far I’ve been able to avoid that complication by just using a list of return values.

Avoiding Phantom Mocks

One of the gotchas of mocking is that you might end up with behavior that’s specified in the mock object, but not really implemented in the real object. The result is that you have code that passes the tests but fails in production. You can prevent this from happening by setting the

# module2.py

from module1 import A

class B(object):

    def __init__(self):
        self.a = A()
2 and
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        self.a = A()
3 parameters in the
               |------------------|
module1.A ---> |                  |
               | <Actual A Class> |
module2.A ---> |                  |
               |------------------|
9 decorator as in line 8 below:

1
2
3
4
5
6
7
8
1
1
2
3
4
5
6
7
8
2

By using

# module2.py

from module1 import A

class B(object):

    def __init__(self):
        self.a = A()
2 above, we are automatically defining the Mock oject with the same specs as the actual
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...
0 class. Likewise, by using
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        self.a = A()
3 above, we are “freezing” the specs of the Mock object such that we don’t accidentally create phantom mock attributes or methods in it.

That’s it for now. If you want to dive deeper into the internals of Python’s mocking library, head on over to .