Py.test 修补模块内对象



>我有一个包:

- 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 提供的基于文件的缓存,但是在运行测试时,我想使用基于内存的缓存来模拟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 运行 test 时,他们仍在使用基于文件的旧缓存。 有什么问题吗?

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

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

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

想象一下以下一组模块:

# 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都已经过monkeypatch,我需要一个为所有这些版本显式设置属性的装置:

# 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 缓存,因此看到上述内容表明它已成功。

最新更新