在py.test测试中重启python(或重装模块)

时间:2016-01-10 11:09:01

标签: python-3.x pytest

我有一个(python3)包具有完全不同的行为,具体取决于它init()的编辑方式(可能不是最好的设计,但重写不是一个选项)。该模块只能init()一次,第二次给出错误。我想使用py.test来测试这个包(两个行为)。

注意:包的性质使得这两种行为互相排斥,没有任何理由可以在单一程序中同时存在这两种行为。

我的测试目录中有多个test_xxx.py模块。每个模块都会按照需要的方式启动包(使用fixture)。由于py.test启动python解释器一次,因此在一次py.test运行中运行所有测试模块失败。

猴子修补程序包以允许第二个init()不是我想要做的事情,因为有内部缓存等可能导致无法解释的行为。

  • 是否可以告诉py.test在单独的python进程中运行每个测试模块(从而不受另一个测试模块中的inits的影响)
  • 有没有办法可靠地重新加载包(包括所有子依赖项等)?
  • 是否有其他解决方案(我正在考虑导入然后在灯具中导入包,但这似乎过多了?)

4 个答案:

答案 0 :(得分:1)

要重新加载模块,请尝试使用库reload()

中的imp

示例:

from imp import reload
import some_lib
#do something
reload(some_lib) 

此外,在新进程中启动每个测试是可行的,但多处理代码对于调试来说是一种痛苦。

实施例

import some_test
from multiprocessing import Manager, Process

#create new return value holder, in this case a list
manager = Manager()
return_value = manager.list()
#create new process
process =  Process(target=some_test.some_function, args=(arg, return_value))
#execute process
process.start()
#finish and return process
process.join()
#you can now use your return value as if it were a normal list, 
#as long as it was assigned in your subprocess

答案 1 :(得分:0)

我遇到了同样的问题,并找到了三种解决方案:

  1. 重新加载(some_lib)
  2. 修补SUT,因为导入的方法是SUT中的键和值,所以您可以修补 SUT。例如,如果您在m1中使用m2的f2,则可以修补m1.f2而不是m2.f2
  3. 导入模块,并使用module.function。

答案 2 :(得分:0)

一旦我遇到类似的问题,虽然设计很糟糕。

@pytest.fixture()
def module_type1():
    mod = importlib.import_module('example')
    mod._init(10)
    yield mod
    del sys.modules['example']

@pytest.fixture()
def module_type2():
    mod = importlib.import_module('example')
    mod._init(20)
    yield mod
    del sys.modules['example']


def test1(module_type1)
    pass

def test2(module_type2)
    pass

示例/init.py具有类似的内容

def _init(val):
    if 'sample' in globals():
        logger.info(f'example already imported, val{sample}' )
    else:
        globals()['sample'] = val
        logger.info(f'importing example with val : {val}')

输出:

importing example with val : 10
importing example with val : 20

不知道您的程序包有多复杂,但是如果它只是全局变量,那么这可能会有所帮助。

答案 3 :(得分:0)

删除所有模块导入以及导入模块的测试导入:

import sys

for key in list(sys.modules.keys()):
    if key.startswith("your_package_name") or key.startswith("test"):
        del sys.modules[key]

您可以通过在 conftest.py 文件中使用 @pytest.fixture 装饰器配置夹具来将其用作夹具。