The*_*ark 8 python fixtures pytest pymongo mongomock
我有连接到 MongoDB 客户端的代码,我正在尝试对其进行测试。为了测试,我不想连接到实际的客户端,所以我试图找出制作一个用于测试目的的假客户端。代码的基本流程是我在某处有一个函数来创建一个pymongo客户端,然后查询它并制作一个在其他地方使用的字典。
我想使用 pytest 编写一些测试来测试将调用get_stuff. 我的问题是get_stuff调用mongo()实际上是连接到数据库的原因。我试图只是使用pytest.fixture(autouse=True)和mongomock.MongoClient()替换mongo().
但这并不能取代mongo_stuff.mongo(). 有什么方法可以告诉 pytest 替换一个函数,以便fixture调用我的函数而不是实际函数?我认为使fixture我的测试mongo()在命名空间中的优先级高于实际模块中的函数。
这是我的示例的示例文件结构:
.
??? project
? ??? __init__.py
? ??? mongo_stuff
? ? ??? __init__.py
? ? ??? mongo_stuff.py
? ??? working_class
? ??? __init__.py
? ??? somewhere_else.py
??? testing
??? __init__.py
??? test_stuff.py
Run Code Online (Sandbox Code Playgroud)
mongo_stuff.py
import pymongo
def mongo():
return pymongo.MongoClient(connection_params)
def get_stuff():
db = mongo() # Makes the connection using another function
stuff = query_function(db) # Does the query and makes a dict
return result
Run Code Online (Sandbox Code Playgroud)
某处_其他.py
from project.mongo_stuff import mongo_stuff
mongo_dict = mongo_stuff.get_stuff()
Run Code Online (Sandbox Code Playgroud)
test_stuff.py
import pytest
import mongomock
@pytest.fixture(autouse=True)
def patch_mongo(monkeypatch):
db = mongomock.MongoClient()
def fake_mongo():
return db
monkeypatch.setattr('project.mongo_stuff.mongo', fake_mongo)
from poject.working_class import working_class # This starts by calling project.mongo_stuff.mongo_stuff.get_stuff()
Run Code Online (Sandbox Code Playgroud)
而这目前会给我一个连接错误,因为connection params在mongo_stuff.py是工作在生产环境中只取得。如果我将test_stuff.py 中的import语句放入测试函数中,则它可以正常工作,并且db 将在测试环境中使用。我也尝试将其更改为也不起作用。mongomocksetattrmonkeypatch.setattr('project.working_class.mongo_stuff.mongo', fake_mongo)
你已经成功了一半:你已经为 db 客户端创建了一个模拟,现在你必须修补mongo_stuff.mongo函数以返回模拟而不是真正的连接:
@pytest.fixture(autouse=True)
def patch_mongo(monkeypatch):
db = mongomock.MongoClient()
def fake_mongo():
return db
monkeypatch.setattr('mongo_stuff.mongo', fake_mongo)
Run Code Online (Sandbox Code Playgroud)
出现连接错误的原因是您somewhere_else在 中的模块级别导入test_stuff,并且somewhere_else也在模块级别运行连接代码。因此,用灯具修补会为时已晚,并且不会产生任何效果。如果要在模块级别导入,则必须在导入之前修补 mongo 客户端somewhere_else。这将避免错误提升,但非常难看:
from project.mongo_stuff import mongo_stuff
import mongomock
import pytest
from unittest.mock import patch
with patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient()):
from project.working_class import somewhere_else
@patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient())
def test_db1(mocked_mongo):
mongo_stuff.mongo()
assert True
@patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient())
def test_db2(mocked_mongo):
somewhere_else.foo()
assert True
Run Code Online (Sandbox Code Playgroud)
您应该尽可能避免在模块级别运行代码,或者在测试中运行在模块级别执行代码的导入(正如您已经在评论中发现的那样)。