使用伪造的mongoDB进行pytest测试 [英] Using a fake mongoDB for pytest testing
问题描述
我有连接到MongoDB客户端的代码,我正在尝试对其进行测试.对于测试,我不想连接到实际的客户端,因此我试图找出一个用于测试目的的假冒产品.代码的基本流程是,我在某处有一个函数,该函数创建一个pymongo
客户端,然后查询该客户端并做出一个在其他地方使用的字典.
I have code that connects to a MongoDB Client and I'm trying to test it. For testing, I don't want to connect to the actual client, so I'm trying to figure out make a fake one for testing purposes. The basic flow of the code is I have a function somewhere the creates a pymongo
client, then queries that and makes a dict that is used elsewhere.
我想使用pytest编写一些测试,这些测试将测试将调用get_stuff
的不同函数和类.我的问题是get_stuff
会调用mongo()
,这实际上是与数据库建立连接的原因.我试图只用pytest.fixture(autouse=True)
和mongomock.MongoClient()
代替mongo()
.
I want to write some tests using pytest that will test different functions and classes that will call get_stuff
. My problem is that get_stuff
calls mongo()
which is what actually makes the connection to the database. I was trying to just use pytest.fixture(autouse=True)
and mongomock.MongoClient()
to replace mongo()
.
但这不能代替mongo_stuff.mongo()
.有什么办法可以告诉pytest替换函数,以便调用我的fixture
而不是实际的函数?我以为制作fixture
会使我的测试mongo()
在名称空间中的优先级高于实际模块中的功能.
But this isn't replacing the mongo_stuff.mongo()
. Is there some way I can tell pytest to replace a function so my fixture
is called instead of the actual function? I thought making the fixture
would put my testing mongo()
higher priority in the namespace than the function in the actual module.
这是我的示例的示例文件结构:
Here is an example file structure with my example:
.
├── project
│ ├── __init__.py
│ ├── mongo_stuff
│ │ ├── __init__.py
│ │ └── mongo_stuff.py
│ └── working_class
│ ├── __init__.py
│ └── somewhere_else.py
└── testing
├── __init__.py
└── test_stuff.py
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
somewhere_else.py
from project.mongo_stuff import mongo_stuff
mongo_dict = mongo_stuff.get_stuff()
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()
这当前会给我一个连接错误,因为 mongo_stuff.py 中的connection params
仅在生产环境中可用.如果我将 test_stuff.py 中的import
语句放入测试函数中,则它可以正常工作,并且mongomock
db将在测试环境中使用.我还尝试将setattr
更改为monkeypatch.setattr('project.working_class.mongo_stuff.mongo', fake_mongo)
,这也行不通.
And this will currently give me a connection error since the connection params
in mongo_stuff.py are only made to work in the production environment. If I put the import
statement from test_stuff.py into a test function, then it works fine and mongomock
db will be used in the testing enviornment. I also tried change the setattr
to monkeypatch.setattr('project.working_class.mongo_stuff.mongo', fake_mongo)
which also does not work.
推荐答案
您已经完成了一半:您已经为db客户端创建了一个模拟,现在您必须修补mongo_stuff.mongo
函数以返回该模拟,而不是返回真正的联系:
You're halfway there: you have created a mock for the db client, now you have to patch the mongo_stuff.mongo
function to return the mock instead of a real connection:
@pytest.fixture(autouse=True)
def patch_mongo(monkeypatch):
db = mongomock.MongoClient()
def fake_mongo():
return db
monkeypatch.setattr('mongo_stuff.mongo', fake_mongo)
出现连接错误的原因是要在test_stuff
的模块级别上导入somewhere_else
,而somewhere_else
也在模块级别上运行连接代码.因此,用灯具打补丁太迟了,不会有任何效果.如果要在模块级别导入,则必须在导入somewhere_else
之前先修补mongo客户端 .这样可以避免出现错误,但是非常丑陋:
The reason why you get the connection error is that you are importing somewhere_else
on module level in test_stuff
, and somewhere_else
runs connection code also on module level. So patching with fixtures will come too late and will have no effect. You have to patch the mongo client before the import of somewhere_else
if you want to import on module level. This will avoid the error raise, but is extremely ugly:
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
您应该尽可能避免在模块级别运行代码,或者在测试内部运行在模块级别执行代码的导入(如您在注释中已经发现的那样).
You should rather avoid running code on module level when possible, or run the imports that execute code on module level inside the tests (as you already found out in the comments).
这篇关于使用伪造的mongoDB进行pytest测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!