python 模块级代码中的测试分支

Testing branches in module-level code in python

我们有一个 python 模块,调用者可以使用它来 运行 Mac OS X 上的一些实用命令。命令的路径及其用法不同在 OS 和我们的模块的版本之间是为了对用户隐藏它。我们在导入模块时确定一次OS的版本,像这样(helper.py,简化以证明这一点):

import platform
from distutils.version import StrictVersion

print('Importing helper module...')

os_version, _, _ = platform.mac_ver()

if StrictVersion(os_version) < StrictVersion('10.10'):
    utility1 = 'utility 1 for 10.9 and below'
else:
    utility1 = 'utility 1 for 10.10 and above'

def run_utility1():
    return 'Running ' + utility1

def run_utility2():
    # ...
    pass

# ... more cool functions ...

现在我们要向该模块添加测试。具体来说,我们想确保 OS X 的所有版本的实用程序 运行s 都是正确的。我想到的方法是在不同的测试中 patch platform.mac_ver() return 一个不同的 OS 版本,并且 assert 我们 运行 正在使用正确的实用程序。像这样:

import mock
import unittest

class HelperTests(unittest.TestCase):
    def test_10_9_utility_is_correct(self):
        with mock.patch('platform.mac_ver', return_value=('10.9', 'foo', 'foo')):
            import helper
            result = helper.run_utility1()
            print(result)
            assert result == 'Running utility 1 for 10.9 and below'

    def test_10_10_utility_is_correct(self):
        with mock.patch('platform.mac_ver', return_value=('10.10', 'foo', 'foo')):
            import helper
            result = helper.run_utility1()
            print(result)
            assert result == 'Running utility 1 for 10.10 and above'

if __name__ == '__main__':
    unittest.main()

但这会导致:

Testing started at 12:16 PM ...
Importing helper module...
Running utility 1 for 10.10 and above
Running utility 1 for 10.10 and above

Process finished with exit code 0

Failure
Traceback (most recent call last):
  File "helper_test.py", line 13, in test_10_9_utility_is_correct
    assert result == 'Running utility 1 for 10.9 and below'
AssertionError

似乎 test_10_10_utility_is_correct 先 运行ning(这是由于测试工具中的方法按字母顺序排列吗?),将 mac_ver() 修补为 return 10.10 然后导入 helper。当 test_10_9_utility_is_correct 运行s helper 没有再次导入时,它会失败,因为它认为它在 10.10.

我知道 python 不会两次导入模块,这太棒了。但这是否意味着我们不能在测试中使用模块级代码中的分支,因为它只会 运行 一次?如果有办法,怎么做?

我考虑过将模块级 OS 版本检查代码包装在一个函数中。这将使模拟变得容易,但随后所有其他函数都必须首先调用它,这似乎是不必要的,因为 OS 版本不太可能在调用之间改变。我还考虑过将每个测试方法移动到它自己的测试模块中,这会导致 helper 被多次导入,但这看起来很笨拙。 helper.py?

中给出的两个分支有没有另一种方法来练习

将你的初始化代码放在一个函数中怎么样?

您必须像这样声明您的全局变量:

import platform
from distutils.version import StrictVersion

utility1 = None
def init_module():
    global utility1 # declare it global to modify it
    print('Importing helper module...')

    os_version, _, _ = platform.mac_ver()

    if StrictVersion(os_version) < StrictVersion('10.10'):
        utility1 = 'utility 1 for 10.9 and below'
    else:
        utility1 = 'utility 1 for 10.10 and above'

init_module() #Call the init function when importing the module

def run_utility1():
    return 'Running ' + utility1

然后在每次新测试时,您可以调用 init 函数:

import mock
import unittest
import helper

class HelperTests(unittest.TestCase):
    def test_10_9_utility_is_correct(self):
        with mock.patch('platform.mac_ver', return_value=('10.9', 'foo', 'foo')):
            helper.init_module()
            result = helper.run_utility1()
            print(result)
            assert result == 'Running utility 1 for 10.9 and below'

    def test_10_10_utility_is_correct(self):
        with mock.patch('platform.mac_ver', return_value=('10.10', 'foo', 'foo')):
            helper.init_module()
            result = helper.run_utility1()
            print(result)
            assert result == 'Running utility 1 for 10.10 and above'

if __name__ == '__main__':
    unittest.main()