'pytest : How to write pytest code to detect a "func" called without actually executing the "func"

pytest : How to write pytest code to detect a "func" called without actually executing the "func" ,

target.py:

import requests
import threading

CFG=None
targets={}

def add_target(func):
    targets[func.__name__] = func
    return func


def parent_func(url):
    for funct in set(targets):
        threading.Thread(target=targets[funct], args=(CFG[url],)).start()

@add_target
def child_func(url):
    try:
        response = requests.request("POST", url,
                                    headers={"Content-Type": "application/json"},
                                    data="{\"text\": \"target py test\"}")
        response.raise_for_status()
    except Exception as err:
        raise RuntimeError(err)

test_target.py:

During testing, I want child_func() to get called but the child_func() body should not get executed. Rather, after running tests, child_func() gets executed & test run results in "AssertionError: False is not true "

from unittest.mock import Mock, patch
import target

# Third-party imports...
from nose.tools import assert_true, assert_is_not_none
target.CFG={'url':"some valid url"}
@patch('target.child_func')
def test_child_func(mock_child_func):
    parent_func("url")
    assert_true(mock_child_func.called)


Solution 1:[1]

First (for the sake of my own sanity) let's whittle this down to an actual MRE -- we don't actually need threading or requests or any of the implementation details of child_func to demonstrate the problem here.

target.py:

targets={}

def add_target(func):
    targets[func.__name__] = func
    return func

def parent_func(url):
    for func in targets.values():
        func(url)

@add_target
def child_func(url):
    raise NotImplementedError("The test shouldn't actually call this function!")

test_target.py:

from unittest.mock import Mock, patch
from target import parent_func

@patch('target.child_func')
def test_parent_func(mock_child_func):
    parent_func("url")
    assert mock_child_func.call_count == 1

Within our test, we want parent_func to call mock_child_func, not the real child_func. But when we run our test, we can quickly see that our patch didn't work:

============================================== short test summary info ===============================================
FAILED test_target.py::test_parent_func - NotImplementedError: The test shouldn't actually call this function!

The reason for this is that parent_func doesn't call child_func, it calls targets['child_func']. Patching 'target.child_func' doesn't modify the actual function, it modifies what the name child_func points to in the target module -- and that name isn't in use in the code under test!

The easiest fix here is to patch target.targets:

from unittest.mock import Mock, patch
from target import parent_func

@patch('target.targets', new_callable=dict)
def test_parent_func(mock_targets):
    mock_targets['child_func'] = Mock()
    parent_func("url")
    assert mock_targets['child_func'].call_count == 1

Now our test passes because when parent_func iterates through its targets, it gets our mock_targets dict, which we can populate with whatever functions/mocks we want.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Samwise