我正在尝试了解mock/monkeypatch/pytest-mock
的功能。
让我知道是否可行。如果不能,请建议我如何测试此代码。
我的代码结构:
/
./app
../__init__.py
../some_module1
.../__init__.py
../some_module2
.../__init__.py
./tests
../test_db.py
在/app/__init__.py
处启动我的应用程序(如果有帮助,则为Flask应用程序),并初始化与MongoDB数据库的数据库连接对象:
# ...
def create_app():
# ...
return app
db_conn = DB()
some_module1
和some_module
导入db_conn
对象并将其用作其功能的一部分:
## some_module1/__init__.py
from app import db_conn
...
db = db_conn.db_name2.db_collection2
def some_func1():
data = db.find()
# check and do something with data
return boolean_result
...
## some_module2/__init__.py
from app import db_conn
...
db = db_conn.db_name1.db_collection1
def some_func2():
data = db.find()
# check and do something with data
return boolean_result
...
在测试中,我想根据从数据库接收到的数据测试代码是否正常运行。
我想模拟数据库,更具体地说是db_conn
对象,因为我不想使用真实的数据库(设置和维护环境需要大量工作)。
关于如何模拟db_conn
的任何建议?
我一直在探索pytest-mock
和magicmock
,但我不认为或不知道如何在测试中模拟db_conn
。
答案 0 :(得分:3)
我相信您不应该在真实的数据库上测试案例,因为如果您使用的是外部依赖项,那么它就不再是单元测试了。
存在to specify return-value
并为Mock
或MagicMock
对象自定义(different return values on each iteration even)的可能性。
from unittest.mock import Mock, patch
from app import db_conn
@patch('app.db_conn.find')
def test_some_func1(db_con_mock):
...
assert ...
请记住,在每个patch
中,您都应指定db_conn
的导入路径-使用db_conn
**的路径(我想是在每个测试中使用不同的路径),而不是在何处定义。
答案 1 :(得分:2)
要回答最初的问题“如何使用pytest-mock或magicmock模拟导入的对象”,您可以执行以下操作:
from unittest import mock # because unittest's mock works great with pytest
def test_some_func1():
with mock.patch('some_module1.db', mock.MagicMock(return_value=...)) as magicmock:
result = some_func1(...)
assert ... e.g. different fields of magicmock
assert expected == result
# or alternatively use annotations
@mock.patch('some_module2.db', mock.MagicMock(return_value=...))
def test_some_func2():
result = some_func2(...)
请注意,您没有修补actual source of db
用于其他用例
我想模拟数据库(使用mongo数据库),更具体地说是“ db_conn”对象
您同样会按照上面链接的提示进行操作:
mock.patch('some_module1.db_conn', mock.MagicMock(return_value=...))
鉴于此,您将在测试中注意到来自db = db_conn.db_name2.db_collection2的db
将创建另一个模拟对象。对该对象的调用也将被记录。这样,您还可以跟踪调用和值分配的历史记录。
此外,请参见an example how to pach mongo db.
有关Flask应用程序的测试,请参见the documentation of flask。还有this is a nice explanation as well, and uses DB connections。
作为一般提示,例如提到的@MikeMajara-将您的代码更多地分成较小的功能,这些功能也易于测试。按照TDD的传统:先编写测试,然后实施,然后进行重构(尤其是DRY!)
答案 2 :(得分:1)
关注点分离。构建仅做一件事的方法。如果要使用TDD,则更多。在您的示例中,some_func2不止一项。您可以重构如下:
rm -rf node_modules && npm i
使用这种方法,您可以轻松地分别测试def get_object_from_db():
return db.find()
def check_condition_on_object(obj):
check something to do with object
return true or false
def some_func2():
obj = get_object_from_db()
check_condition_on_object(obj)
和get_object_from_db
。这样可以提高可读性,避免错误,并有助于在某些时候出现这些错误。
关于“模拟导入的对象” 。您可能正在尝试使用一个库来模拟一个对象,该库比您准备的库更高级。这些库为您提供了一些可能不需要的围绕测试环境的方法。从外观上,您只想用模拟数据填充对象,和/或与模拟的db_connection实例进行交互。所以...
要填充,我将简化:您知道要测试的条件,并且要检查给定对象的结果是否为预期的结果。只需构建一个check_condition_on_object
即可返回test_object_provider.py
的已知案例。无需使事情变得更复杂。
要使用伪造的MongoDB连接,您可以尝试使用mongomock。 (尽管理想情况下,您将在单独的测试中使用真实实例测试mongo。