Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the reason that mock.patch ignores a fully imported function?

Today I realized that it matters for unittest.mock.patch how I import a function. Depending on the used way a mock.patch call worked or was ignored. In Python we typically import a function with:

  • an import statement like import os or
  • a from ... import ... statement like from os import system

A mock.patch works like a charm if I use import os, but it was ignored if I patch a from os import system.

Example 1: Using import

import os
from unittest import mock


def echo():
    os.system('echo "Hello"')


with mock.patch('os.system') as mocked:
    print(mocked)
    mocked.side_effect = Exception('Patch works!')
    echo()

Output of example 1

<MagicMock name='system' id='140037358656760'>

Traceback (most recent call last):
  File "/.../config/scratches/scratch_7.py", line 12, in <module>
    echo()
  File "/.../config/scratches/scratch_7.py", line 6, in echo
    os.system('echo "Hello"')
  File "/.../python3.5/unittest/mock.py", line 917, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/.../python3.5/unittest/mock.py", line 973, in _mock_call
    raise effect
Exception: Patch works!

Example 2: Using a full function import and from-import

When I fully import os.system the mock.patch ignores the mocked.side_effect.

from os import system
from unittest import mock


def echo():
    system('echo "Hello"')


with mock.patch('os.system') as mocked:
    print(mocked)
    mocked.side_effect = Exception('Patching does not work!')
    echo()

    print('Patch was ignored!')

Output of example 2

<MagicMock name='system' id='139851175427376'>
Hello
Patch was ignored!

In both cases I don't receive an error and mock could find os.system as a valid path. However, in the second case the function is not properly patched.

  • Why mock.patch does not patch the function in the second example?
  • Are there any implementation specific reasons why the second patch did not work?
like image 247
Jon Avatar asked Jul 31 '18 13:07

Jon


People also ask

What does mocker patch do?

patch() unittest. mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.

Can you mock an import in Python?

Rather than hacking on top of Python's import machinery, you can simply add the mocked module into sys. path , and have Python prefer it over the original module. Now, when the test suite is run, the mocked-lib subdirectory is prepended into sys. path and import A uses B from mocked-lib .

What is the difference between mock and MagicMock?

So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.

What is the difference between mock and patch?

Mock is a type, and patch is a context. So you are going to pass or receive Mock instances as parameters, and apply patch contexts to blocks of code. (Lowercase 'mock' is just the name of the package.) Tests and test classes are often decorated with calls to patch.


1 Answers

When you do from os import system, you get a variable named system pointing to os.system function. Later, you assign, via patching, a different function to os.system, but system keeps to point to the old function. This is the same reason why the following works:

tmp = a
a = b
b = tmp

It doesn't happen in the first example, because you reference os.system before it is mocked. To fix your second example, I'd go with the following:

from os import system
from unittest import mock

def echo():
    system('echo "Hello"')

with mock.patch('__main__.system') as mocked:
    print(mocked)
    mocked.side_effect = Exception('Patching does not work!')
    echo()

    print('Patch was ignored!')

This way you make sure you patch the right reference. This is a rather common pattern. If echo function were in a file named echo.py, the patch call would look like with mock.patch('echo.system').

like image 166
u354356007 Avatar answered Sep 28 '22 06:09

u354356007