模拟urlopen在未知的子包

时间:2018-02-04 23:29:10

标签: python mocking urllib

我想在模块中模拟对urllib.request.urlopen的调用。当它是单个文件时它可以工作,但是当我把它放在一个包中并导入包__init__.py中的模块时,我就不能再把它嘲笑了。

生殖

想象一下,test包中有两个模块:

  • module.py

    from urllib.request import urlopen
    
    def do_it():
        print(urlopen.__module__)
        urlopen('test')
    
  • module_test.py

    from unittest import mock
    from .module import do_it
    
    
    def test_not_working():
        with mock.patch('urllib.request.urlopen', lambda url: print(url)):
            do_it()
    
    
    def test_plain():
        with mock.patch('test.module.urlopen', lambda url: print(url)):
            do_it()
    

test_not_working打印urllib.request作为urlopen的模块,因为我修补了测试模块中的本地urlopen函数,测试失败,因为test不是有效的网址 test_plain打印.module,因为我成功修补urlopen,测试成功并打印test

我的问题是我已将.module移到了一个包中,因为我想将我创建的多个文件分组。它现在看起来像这样:

  • module

    • __init__.py

      from .module import do_it
      
    • module.py(与之前的module.py相同)
  • test_module.py

    from unittest import mock
    from .module import do_it
    
    
    def test_packaged_fails():
        with mock.patch('test.module.urlopen', lambda url: print(url)):
            do_it()
    
    def test_packaged_works():
        with mock.patch('test.module.module.urlopen', lambda url: print(url)):
            do_it()
    

前两个测试保持不变,但test_packaged打印urllib.request,因第一次测试失败,因为网址test无效。

我知道我没有模仿urlopen,因为它显然不使用test.module.urlopen而是使用test.module.module.urlopen

限制

实际项目

我不知道如何解决这个问题,因为该模块只是开源项目(OpenMensa Parsers)中的众多模块之一。 Aachener parser是一个包含多个文件的包,而不是像其他解析器一样的单个文件模块 当我想要upgrade snapshots进行回归测试时,会出现上述问题。它应该缓存解析器发出的所有请求,以便稍后可以再现测试,即使网站发生了变化。

列表

我有以下限制:

  • 我无法硬编码子模块(test.module.module.urlopen)的路径,因为我也希望将快照生成用于其他解析器。
  • 由于构建系统和部署,我只能使用可用作Debian Wheezy软件包的库。 See the currently installed dependencies.
  • 我希望与urllib.request.urlopen保持一致以与其他解析器保持一致。
  • 我实际上并没有直接致电do_it()。我从另一个模块调用一个函数,该模块从不同的解析器模块动态导入do_it(),然后调用它。为简单起见,我将保留上面的最小例子。

问题

如果我不知道调用它的子包,我如何在包中修补urlopen

2 个答案:

答案 0 :(得分:0)

解决方法是修改module/module.py中的import语句:

from urllib import request

def do_it():
    print(urlopen.__module__)
    request.urlopen('test')

这使得可以使用:

来模拟它
with mock.patch('urllib.request.urlopen', lambda url: print(url)):
    do_it()

但是,这是一种需要更改被测模块的解决方法。我仍然对找到直接导入urlopen并在那里模拟它的子模块的方式感兴趣。

答案 1 :(得分:0)

如果您知道哪个函数使用urlopen,并且您知道它通过模块级urlopen访问from urllib.request import urlopen,那么您可以通过函数的全局变量dict执行补丁:< / p>

with mock.patch.dict(do_it.__globals__, urlopen=whatever):
    do_whatever_with(do_it)

最终,在Python中进行模拟仍然需要大量有关测试代码导入的实现细节知识。