-1

In the docs, it explains why patching at at the function definition level doesn't work:

Imagine we have a project that we want to test with the following structure:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

Now we want to test some_function but we want to mock out SomeClass using patch(). The problem is that when we import module b, which we will have to do then it imports SomeClass from module a. If we use patch() to mock out a.SomeClass then it will have no effect on our test; module b already has a reference to the real SomeClass and it looks like our patching had no effect.

The core explanation is "module b already has a reference to the real SomeClass", but I don't fully understand the concept here. Can someone give me a deeper explanation?

Makoto
  • 104,088
  • 27
  • 192
  • 230
user1187968
  • 7,154
  • 16
  • 81
  • 152
  • 1
    Does this answer your question? [Unit Test Behavior with Patch (Flask)](https://stackoverflow.com/questions/29834693/unit-test-behavior-with-patch-flask) – Makoto Mar 01 '23 at 19:49

2 Answers2

0

The key is understanding what name some_function uses to access the class, not where the class is defined.

There are two ways you could have written some_function:

  1. Accessing an attribute from the module a

    import a
    
    def some_function():
        x = a.SomeClass()
        ...
    
  2. Accessing a global variable defined in b

    from a import SomeClass
    
    def some_function():
        x = SomeClass()
    

In the first scenario, you would need to change what a.SomeClass refers to. In the second scenario, you need to change what b.SomeClass refers to.

chepner
  • 497,756
  • 71
  • 530
  • 681
0

An analogy:

a = {'SomeClass': 1}

# importing `from a import SomeClass`
b_SomeClass = a['SomeClass']

# patching
a['SomeClass'] = 2

print(b_SomeClass)  # still 1, because `b_SomeClass` is unrelated to the dict
a = {'SomeClass': 1}

# importing `import a`
b_a = a

# patching
a['SomeClass'] = 2

print(b_a['SomeClass'])  # 2, because we are referencing the thing 
                         # that changed: the dict

Modules are just objects in Python, so this is an especially close analogy.

Ry-
  • 218,210
  • 55
  • 464
  • 476