Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 3.x 如何重定向硬编码调用以打开自定义文件?_Python 3.x_Unit Testing - Fatal编程技术网

Python 3.x 如何重定向硬编码调用以打开自定义文件?

Python 3.x 如何重定向硬编码调用以打开自定义文件?,python-3.x,unit-testing,Python 3.x,Unit Testing,我编写了一些python代码,需要读取/etc/myapp/config.conf中的配置文件。我想写一个单元测试,看看如果文件不存在,或者包含坏值,通常会发生什么。让我们假设它看起来像这样 """ myapp.py """ def readconf() """ Returns string of values read from file """ s = '' with open('/etc/myapp/config.conf', 'r') as f:

我编写了一些python代码,需要读取/etc/myapp/config.conf中的配置文件。我想写一个单元测试,看看如果文件不存在,或者包含坏值,通常会发生什么。让我们假设它看起来像这样

""" myapp.py
"""
def readconf()
    """ Returns string of values read from file
    """

    s = ''
    with open('/etc/myapp/config.conf', 'r') as f:
        s = f.read()
    return s
然后我还有其他代码,它解析
s
的值


我可以通过一些神奇的Python功能,调用
readconf
open
重定向到作为测试环境一部分设置的自定义位置吗

您可以将配置文件外部化或参数化,而不是硬编码配置文件。有两种方法可以做到这一点:

  • 环境变量:使用包含配置文件位置的
    $CONFIG
    环境变量。您可以使用可以使用
    os.environ['CONFIG']
    设置的环境变量运行测试
  • CLI参数:使用命令行参数初始化模块。对于测试,您可以设置sys.argv,并让config属性由该属性设置
  • 例如:

    main.py test.py
    为了在函数中模拟对
    open
    的调用,而不是像Nf4r的回答那样用助手函数替换调用,您可以使用自定义补丁上下文管理器:

    from contextlib import contextmanager
    from types import CodeType
    
    
    @contextmanager
    def patch_call(func, call, replacement):
        fn_code = func.__code__
        try:
            func.__code__ = CodeType(
                fn_code.co_argcount,
                fn_code.co_kwonlyargcount,
                fn_code.co_nlocals,
                fn_code.co_stacksize,
                fn_code.co_flags,
                fn_code.co_code,
                fn_code.co_consts,
                tuple(
                    replacement if call == name else name
                    for name in fn_code.co_names
                ),
                fn_code.co_varnames,
                fn_code.co_filename,
                fn_code.co_name,
                fn_code.co_firstlineno,
                fn_code.co_lnotab,
                fn_code.co_freevars,
                fn_code.co_cellvars,
            )
            yield
        finally:
            func.__code__ = fn_code
    
    现在,您可以修补您的功能:

    def patched_open(*args):
        raise FileNotFoundError
    
    with patch_call(readconf, "open", "patched_open"):
        ...
    

    您可以使用mock来修补内置“open”模块的实例,以重定向到自定义函数

    """ myapp.py
    """
    def readconf():
        s = ''
        with open('./config.conf', 'r') as f:
            s = f.read()
        return s
    
    
    """ test_myapp.py
    """
    import unittest
    from unittest import mock
    
    import myapp
    
    
    def my_open(path, mode):
        return open('asdf', mode)
    
    class TestSystem(unittest.TestCase):
    
        @mock.patch('myapp.open', my_open)
        def test_config_not_found(self):
            try:
                result = myapp.readconf()
                assert(False)
            except FileNotFoundError as e:
                assert(True)
    
    
    if __name__ == '__main__':
        unittest.main()
    
        @mock.patch('myapp.open', lambda path, mode: open('asdf', mode))
        def test_config_not_found(self):
            ...
    
    如果您不想声明另一个函数,也可以使用这样的lambda

    """ myapp.py
    """
    def readconf():
        s = ''
        with open('./config.conf', 'r') as f:
            s = f.read()
        return s
    
    
    """ test_myapp.py
    """
    import unittest
    from unittest import mock
    
    import myapp
    
    
    def my_open(path, mode):
        return open('asdf', mode)
    
    class TestSystem(unittest.TestCase):
    
        @mock.patch('myapp.open', my_open)
        def test_config_not_found(self):
            try:
                result = myapp.readconf()
                assert(False)
            except FileNotFoundError as e:
                assert(True)
    
    
    if __name__ == '__main__':
        unittest.main()
    
        @mock.patch('myapp.open', lambda path, mode: open('asdf', mode))
        def test_config_not_found(self):
            ...
    

    查看
    unittest.mock.patch
    如@l3via所述,使用
    unittest.mock.patch
    。最好将带有open()的
    作为f.read():
    代码移动到分离函数,如
    读取文件(路径)
    ,然后模拟此特定函数,在调用时引发
    FileNotFound
    异常。