Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.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 “如何测试或模拟”;如果uuu name uuuu==&x27__主&'&引用;目录_Python_Unit Testing_Testing_Mocking_Python Import - Fatal编程技术网

Python “如何测试或模拟”;如果uuu name uuuu==&x27__主&'&引用;目录

Python “如何测试或模拟”;如果uuu name uuuu==&x27__主&'&引用;目录,python,unit-testing,testing,mocking,python-import,Python,Unit Testing,Testing,Mocking,Python Import,假设我有一个包含以下内容的模块: def main(): pass if __name__ == "__main__": main() 我想为下半部分编写一个单元测试(我希望实现100%的覆盖率)。我发现了执行import/\uuuuu name\uuuu设置机制的runpy内置模块,但我不知道如何模拟或以其他方式检查main()函数是否被调用 这就是我迄今为止所尝试的: import runpy import mock @mock.patch('foobar.main')

假设我有一个包含以下内容的模块:

def main():
    pass

if __name__ == "__main__":
    main()
我想为下半部分编写一个单元测试(我希望实现100%的覆盖率)。我发现了执行import/
\uuuuu name\uuuu
设置机制的runpy内置模块,但我不知道如何模拟或以其他方式检查main()函数是否被调用

这就是我迄今为止所尝试的:

import runpy
import mock

@mock.patch('foobar.main')
def test_main(self, main):
    runpy.run_module('foobar', run_name='__main__')
    main.assert_called_once_with()

您可以使用
imp
模块而不是
import
语句来执行此操作。
import
语句的问题在于,
“\uuuuu main\uuuuuuu”
的测试作为import语句的一部分运行,然后才有机会分配给
runpy.\uuuuu name\uuuuu

例如,您可以像这样使用
imp.load\u source()

import imp
runpy = imp.load_source('__main__', '/path/to/runpy.py')
with patch.object(module, "main", MagicMock(return_value='foo')) as mock_main:

第一个参数被分配给导入模块的
\uuu name\uuuu

一种方法是将模块作为脚本(例如os.system(…)运行,并将它们的stdout和stderr输出与预期值进行比较。

我将选择另一种替代方法,即如果覆盖率报告中的
,当然,只有在测试中已经有main()函数的测试用例时,才能这样做

至于我为什么选择排除而不是为整个脚本编写新的测试用例,是因为如果正如我所说的那样,您已经为
main()
函数添加了一个测试用例,那么为脚本添加另一个测试用例(仅用于100%覆盖率)的事实将是重复的

关于如何排除
如果_uname__=='\u u main___',您可以编写一个覆盖率配置文件并添加到部分报告中:

[report]

exclude_lines =
    if __name__ == .__main__.:
可以找到有关覆盖率配置文件的更多信息


希望这能有所帮助。

哇,我来晚了一点,但我最近遇到了这个问题,我想我想出了一个更好的解决方案,所以这里是

我正在开发一个模块,该模块包含十几个脚本,所有脚本都以以下内容结尾:

if __name__ == '__main__':
    if '--help' in sys.argv or '-h' in sys.argv:
        print(__doc__)
    else:
        sys.exit(main())
当然不可怕,但也不可测试。我的解决方案是在我的一个模块中编写一个新函数:

def run_script(name, doc, main):
    """Act like a script if we were invoked like a script."""
    if name == '__main__':
        if '--help' in sys.argv or '-h' in sys.argv:
            sys.stdout.write(doc)
        else:
            sys.exit(main())
然后将此gem放在每个脚本文件的末尾:

run_script(__name__, __doc__, main)
从技术上讲,无论脚本是作为模块导入还是作为脚本运行,此函数都将无条件运行。但是,这是可以的,因为除非脚本作为脚本运行,否则函数实际上不会执行任何操作。因此,代码覆盖率看到函数运行并说“是的,100%的代码覆盖率!”同时,我编写了三个测试来覆盖函数本身:

@patch('mymodule.utils.sys')
def test_run_script_as_import(self, sysMock):
    """The run_script() func is a NOP when name != __main__."""
    mainMock = Mock()
    sysMock.argv = []
    run_script('some_module', 'docdocdoc', mainMock)
    self.assertEqual(mainMock.mock_calls, [])
    self.assertEqual(sysMock.exit.mock_calls, [])
    self.assertEqual(sysMock.stdout.write.mock_calls, [])

@patch('mymodule.utils.sys')
def test_run_script_as_script(self, sysMock):
    """Invoke main() when run as a script."""
    mainMock = Mock()
    sysMock.argv = []
    run_script('__main__', 'docdocdoc', mainMock)
    mainMock.assert_called_once_with()
    sysMock.exit.assert_called_once_with(mainMock())
    self.assertEqual(sysMock.stdout.write.mock_calls, [])

@patch('mymodule.utils.sys')
def test_run_script_with_help(self, sysMock):
    """Print help when the user asks for help."""
    mainMock = Mock()
    for h in ('-h', '--help'):
        sysMock.argv = [h]
        run_script('__main__', h*5, mainMock)
        self.assertEqual(mainMock.mock_calls, [])
        self.assertEqual(sysMock.exit.mock_calls, [])
        sysMock.stdout.write.assert_called_with(h*5)

该死!现在,您可以编写一个可测试的
main()
,将其作为脚本调用,具有100%的测试覆盖率,并且不需要忽略覆盖率报告中的任何代码。

我的解决方案是使用
imp.load\u source()
并通过不提供所需的CLI参数来强制在
main()
早期引发异常,提供格式不正确的参数,设置路径时无法找到所需的文件,等等

import imp    
import os
import sys

def mainCond(testObj, srcFilePath, expectedExcType=SystemExit, cliArgsStr=''):
    sys.argv = [os.path.basename(srcFilePath)] + (
        [] if len(cliArgsStr) == 0 else cliArgsStr.split(' '))
    testObj.assertRaises(expectedExcType, imp.load_source, '__main__', srcFilePath)
然后在测试类中,您可以像这样使用此函数:

def testMain(self):
    mainCond(self, 'path/to/main.py', cliArgsStr='-d FailingArg')

我发现这个解决方案很有帮助。如果您使用一个函数来保存所有的脚本代码,那么这个函数会很好地工作。 代码将作为一个代码行处理。是否为覆盖率计数器执行整行并不重要(尽管这并不是您实际期望的100%覆盖率) 这个把戏也被接受了。;-)

Python 3解决方案:

import os
from importlib.machinery import SourceFileLoader
from importlib.util import spec_from_loader, module_from_spec
from importlib import reload
from unittest import TestCase
from unittest.mock import MagicMock, patch
    

class TestIfNameEqMain(TestCase):
    def test_name_eq_main(self):
        loader = SourceFileLoader('__main__',
                                  os.path.join(os.path.dirname(os.path.dirname(__file__)),
                                               '__main__.py'))
        with self.assertRaises(SystemExit) as e:
            loader.exec_module(module_from_spec(spec_from_loader(loader.name, loader)))
使用定义自己的小函数的替代解决方案:

# module.py
def main():
    if __name__ == '__main__':
        return 'sweet'
    return 'child of mine'
您可以使用以下工具进行测试:

# Override the `__name__` value in your module to '__main__'
with patch('module_name.__name__', '__main__'):
    import module_name
    self.assertEqual(module_name.main(), 'sweet')

with patch('module_name.__name__', 'anything else'):
    reload(module_name)
    del module_name
    import module_name
    self.assertEqual(module_name.main(), 'child of mine')

我不想排除有问题的行,所以基于,我实现了一个简化版本的

  • 我将
    if\uuuuuu name\uuuuu==“\uuuuu main\uuuuu”:
    包装在一个函数中,使其易于测试,然后调用该函数以保留逻辑:
  • 我使用
    unittest.mock
    模拟了
    \uuuu name\uuuuu
    ,以获得有问题的行:
  • 如果将参数发送到模拟函数中,如下所示

    if __name__ == "__main__":
        main(main_args)
    
    然后,您可以使用
    assert\u调用\u once\u with()
    进行更好的测试:

    expected_args = ["expected_arg_1", "expected_arg_2"]
    mock_main.assert_called_once_with(expected_args)
    
    如果需要,还可以向
    MagicMock()
    添加
    return\u值,如下所示:

    import imp
    runpy = imp.load_source('__main__', '/path/to/runpy.py')
    
    with patch.object(module, "main", MagicMock(return_value='foo')) as mock_main:
    

    在子进程中运行脚本并期望coverage.py跟踪执行的行并不像听起来那么容易,可以在这里找到使此解决方案工作的更多信息:imp模块的工作方式似乎与我在问题中使用的runpy模块非常相似。问题是,在加载模块之后和运行代码之前(显然)无法插入mock。你对此有什么建议吗?嘿,我添加了一个新的答案,它提供了100%的测试覆盖率(包括测试!),并且不需要忽略任何内容。让我知道你的想法:谢谢。对于那些想知道的人:
    nose-cov
    在下面使用coverage.py,所以一个包含上述内容的
    .coverage.c
    文件就可以了。IMHO,即使我觉得它有趣和有用,这个答案实际上并没有对OP做出响应。他想测试main的调用,不要跳过这张支票。否则,脚本实际上可以做除实际期望之外的所有事情,当启动时,测试会说“OK,一切正常!”。主函数可以进行完整的单元测试,即使从未被实际调用过。它可能不会对OP做出响应,但对于实际目的来说,这是一个很好的答案,至少我是这样发现这个问题的。类似的解决方案是使用
    #pragma:no cover
    ,类似于
    如果uuu name uuu=='uuu main:#pragma:no cover
    。就我个人而言,我不愿意这样做,因为它会使代码混乱,而且非常难看,所以我认为mouad的答案是最好的解决方案,但其他人可能会发现它很有用。@mouad如果我们非常具体,我认为从技术上讲,正则表达式行应该使用
    [']
    而不是
    比如:
    \uuuuu name\uuu==[']\ uu main\uu[']:
    。我很欣赏在寻找sol时的创造力和毅力
    with patch.object(module, "main", MagicMock(return_value='foo')) as mock_main: