Search code examples
pythonunit-testingpytestpython-unittestpython-mock

Mock a function present inside a list in pytest


I want to mock a function present inside a list and check whether it has been called at least once. Below is a similar implementation I tried:-

In fun_list.py (funA and funB are two functions in other_module)

import other_module

FUN_LIST = [
    other_module.funA,
    other_module.funB,
]

def run_funs():
    for fun in FUN_LIST:
        fun()

In demo.py

from fun_list import run_funs

def run_demo():
    ...
    run_funs()
    ...

In test_demo.py

from demo import run_demo

@patch('other_module.funB')
def test_demo_funs(mocked_funB):
    mocked_funB.return_value = {}
    run_demo()

    assert mocked_funB.called

In above case, I'm trying to mock funB in other_module but the function doesn't get mocked and the cursor gets inside the actual funB in other_module. Thus, the assert mocked_funB.called returns false.

Any lead on how I can mock other_module.funB ? I have found a similar question on StackOverflow but that went unanswered, so decided to post my version of it.

Any help will be appreciated, thank you in advance.


Solution

  • You need to mock before importing the module under test. The code in the module scope will be executed when import the module. It is too late to mock through the decorator when the test case is executed.

    E.g.

    other_module.py:

    def funA():
        pass
    
    
    def funB():
        pass
    

    fun_list.py:

    import other_module
    
    print('execute module scope code')
    
    FUN_LIST = [
        other_module.funA,
        other_module.funB,
    ]
    
    
    def run_funs():
        for fun in FUN_LIST:
            fun()
    

    demo.py:

    from fun_list import run_funs
    
    
    def run_demo():
        run_funs()
    

    test_demo.py:

    import unittest
    from unittest.mock import patch
    
    
    class TestDemo(unittest.TestCase):
        @patch('other_module.funB')
        def test_demo_funs(self, mocked_funB):
            print('mock before import the module')
            from demo import run_demo
            mocked_funB.return_value = {}
            run_demo()
            assert mocked_funB.called
    
    
    if __name__ == '__main__':
        unittest.main()
    

    test result:

    mock before import the module
    execute module scope code
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.002s
    
    OK
    Name                                         Stmts   Miss  Cover   Missing
    --------------------------------------------------------------------------
    src/stackoverflow/67563601/demo.py               3      0   100%
    src/stackoverflow/67563601/fun_list.py           6      0   100%
    src/stackoverflow/67563601/other_module.py       4      1    75%   6
    src/stackoverflow/67563601/test_demo.py         12      0   100%
    --------------------------------------------------------------------------
    TOTAL                                           25      1    96%