【问题标题】:Py.test patching a in-module objectPy.test 修补模块内对象
【发布时间】:2014-12-15 06:27:08
【问题描述】:

我有一个包裹:

- package/
  - __init__.py
  - cache.py
  - module1.py
  - module2.py
- tests/
  - test_module1.py
  - test_module2.py
  - conftest.py

module1module2 都从 cache.py 导入:

from package.cache import cache

@cache()
def foo():
   ...

默认情况下,cache 使用dogpile.cache 提供的基于文件的缓存,但是在运行测试时,我想使用dogpile.cache 也支持的基于内存的缓存来模拟cache

这就是我想要的:

# conftest.py

import pytest

@pytest.fixture(autouse=True)
def patch_cache(monkeypatch):
    from dogpile.cache import make_region
    m_cache = make_region().configure('dogpile.cache.memory')
    monkeypatch.setattr('package.cache.cache', m_cache)

如您所见,我创建了一个夹具,其中使用 monkeypatchcache 替换为 m_cache

但是,这不起作用,当我使用py.test 运行测试时,他们仍在使用基于旧文件的缓存。有什么问题吗?

【问题讨论】:

    标签: python unit-testing testing pytest


    【解决方案1】:

    @cache() 在导入模块时应用,因为装饰器是在模块的顶层调用的。如果您在导入该模块后对其进行monkeypatch,则不会应用您的补丁版本。

    【讨论】:

    • 我不认为pytest 的魔力在技术上是正确的。您实际上可以修改模块级别的内容,因为在解析测试和导入应用程序代码之前运行夹具:这很奇怪且令人惊讶。我认为这里的问题是在导入站点进行猴子补丁。
    【解决方案2】:

    我最近在 pytest 和 monkeypatching 方面遇到了类似的问题,我认为应该通过使用 monkeypatch 来解决。我试图将我的烧瓶应用程序中的缓存换成内存中的缓存,这样我的缓存视图和其他东西就不会意外地破坏真正的应用程序缓存键。

    我发现的问题是,就像unittest.mock.patch 一样,我必须对正在导入和使用已修补的东西的位置进行monkeypatch,换句话说,就是调用站点。

    想象以下一组模块:

    # package1/app1/module1.py
    
    from flask_app import cache
    cache.get("SOMEKEY")
    
    
    # package1/app2/module1.py
    
    from flask_app import cache
    cache.get("SOMEKEY")
    
    
    # package1/app2/module2.py
    
    from package1.app2.module1 import cache
    cache.get("SOMEKEY")
    

    现在,在 pytest 中,为了保证所有这些不同版本的 cache 都是 monkeypatched,我需要一个为所有这些显式设置属性的夹具:

    # conftest.py
    from werkzeug.contrib.cache import SimpleCache
    
    @pytest.fixture(scope="function", autouse=True)
    def safe_cache(request, monkeypatch):
        """
        Monkeypatch the cache so it doesn't clobber real keys.
    
        Clear after every test.
        """
        cache = SimpleCache()
        monkeypatch.setattr('package1.app1.module1.cache', cache)
        monkeypatch.setattr('package1.app2.module1.cache', cache)
        monkeypatch.setattr('package1.app2.module2.cache', cache)    
    
        def teardown():
            cache.clear()
        request.addfinalizer(teardown)
    
        return cache
    

    这有点烦人,因为每次我编写一个导入缓存的新模块时,我还必须在猴子补丁这些缓存的夹具中对其进行猴子补丁。

    但它确实有效。

    当我在其中一个导入缓存的模块中设置断点并检查它正在使用什么时,我看到以下内容:

    ipdb> cache
    <werkzeug.contrib.cache.SimpleCache object at 0x10d658ac8>
    

    否则我的烧瓶应用程序正在使用 Redis 缓存,所以看到上面的内容表明它已经成功。

    【讨论】:

      猜你喜欢
      • 2020-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-31
      • 2011-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多