使用假 mongoDB 进行 pytest 测试
Using a fake mongoDB for pytest testing
我有连接到 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
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)
,这也不起作用。
您已经完成了一半:您已经为数据库客户端创建了一个模拟,现在您必须将 mongo_stuff.mongo
函数修补到 return 模拟而不是真正的连接:
@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
运行 的连接代码也在模块中等级。所以用fixtures修补会来得太晚而且不会有任何效果。如果要在模块级别导入,则必须在 导入 somewhere_else
之前修补 mongo 客户端 。这将避免引发错误,但非常丑陋:
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
您应该尽可能避免 运行 模块级别的代码,或者 运行 在测试中在模块级别执行代码的导入(正如您已经在评论中发现的那样)。
我有连接到 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
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)
,这也不起作用。
您已经完成了一半:您已经为数据库客户端创建了一个模拟,现在您必须将 mongo_stuff.mongo
函数修补到 return 模拟而不是真正的连接:
@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
运行 的连接代码也在模块中等级。所以用fixtures修补会来得太晚而且不会有任何效果。如果要在模块级别导入,则必须在 导入 somewhere_else
之前修补 mongo 客户端 。这将避免引发错误,但非常丑陋:
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
您应该尽可能避免 运行 模块级别的代码,或者 运行 在测试中在模块级别执行代码的导入(正如您已经在评论中发现的那样)。