代码之家  ›  专栏  ›  技术社区  ›  lmiguelvargasf

如何模拟在具有相同名称的模块内的函数中调用的函数?

  •  2
  • lmiguelvargasf  · 技术社区  · 6 年前

    我想用 unittest.mock

    AttributeError:没有“get\u\u frequency”属性

    我有以下文件结构:

    pledges/views/
    ├── __init__.py
    ├── util.py
    └── user_profile.py
    pledges/tests/unit/profile
    ├── __init__.py
    └── test_user.py
    

    pledges/views/__init___.py 我有:

    from .views import *
    from .account import account
    from .splash import splash
    from .preferences import preferences
    from .user_profile import user_profile
    

    在里面, user_profile.py 我有一个函数叫做 user_profile 调用内部函数 util.py get_pledge_frequency

    def user_profile(request, user_id):
        # some logic
    
        # !!!!!!!!!!!!!!!!
        a, b = get_pledge_frequency(parameter) # this is the function I want to mock
    
        # more logic
    
        return some_value
    

    我里面有个测试 test_user.py 具体如下:

    def test_name():
        with mock.patch(
            "pledges.views.user_profile.get_pledge_frequency"
        ) as get_pledge_frequency:
            get_pledge_frequency.return_value = ([], [])
            response = c.get(
                reverse("pledges:user_profile", kwargs={"user_id": user.id})
                ) # this calls the function user_profile inside pledges.user_profile
    
         # some asserts to verify functionality
    

    __init__ 文件。

    那么,有没有办法解决这个问题呢?我已经基本上重命名了文件 用户_轮廓.py profile 然后我修改了测试来引用这个模块中的函数,但是我想知道是否有可能保持函数和模块的名称相同。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Stephen Rauch Afsar Ali    6 年前

    事实证明,可以模拟在具有相同名称的模块中的函数中调用的函数。 一个小包装 unittest.mock.patch()

    代码:

    from unittest import mock
    import importlib
    
    def module_patch(*args):
        target = args[0]
        components = target.split('.')
        for i in range(len(components), 0, -1):
            try:
                # attempt to import the module
                imported = importlib.import_module('.'.join(components[:i]))
    
                # module was imported, let's use it in the patch
                patch = mock.patch(*args)
                patch.getter = lambda: imported
                patch.attribute = '.'.join(components[i:])
                return patch
            except Exception as exc:
                pass
    
        # did not find a module, just return the default mock
        return mock.patch(*args)
    

    使用方法:

    而不是:

    mock.patch("module.a.b")
    

    您需要:

    module_patch("module.a.b")
    

    这是怎么回事?

    基本思想是尝试从最长的模块路径向最短的路径导入模块,如果导入成功,则将该模块用作修补对象。

    import module
    
    print('module.a(): ', module.a())
    print('module.b(): ', module.b())
    print('--')
    
    with module_patch("module.a.b") as module_a_b:
        module_a_b.return_value = 'new_b'
        print('module.a(): ', module.a())
        print('module.b(): ', module.b())
    
    try:
        mock.patch("module.a.b").__enter__()
        assert False, "Attribute error was not raised, test case is broken"
    except AttributeError:
        pass
    

    module

    # __init__.py
    from .a import a
    from .a import b
    
    
    # a.py
    def a():
        return b()
    
    def b():
        return 'b'
    

    结果:

    module.a():  b
    module.b():  b
    --
    module.a():  new_b
    module.b():  b