[英]How to mock a imported object with pytest-mock or magicmock
I am trying to understand the mock/monkeypatch/pytest-mock
capabilities.我正在尝试了解
mock/monkeypatch/pytest-mock
功能。
Let me know if this is possible.让我知道这是否可能。 If not could you please suggest how I can test this code.
如果不能,请建议我如何测试此代码。
My code structure:我的代码结构:
/
./app
../__init__.py
../some_module1
.../__init__.py
../some_module2
.../__init__.py
./tests
../test_db.py
The /app/__init__.py
is where my application (a Flask application if it helps) is started along with initializing a database connection object to a MongoDB database: /app/__init__.py
是我的应用程序(如果有帮助,则为 Flask 应用程序)以及初始化数据库连接 object 到 MongoDB 数据库的位置:
# ...
def create_app():
# ...
return app
db_conn = DB()
The some_module1
and some_module
import the db_conn
object and use it as part of their functions: some_module1
和some_module
导入db_conn
object 并将其用作其功能的一部分:
## 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
...
In my tests, I want to test if my code works properly based on the data received from the database.在我的测试中,我想根据从数据库接收到的数据来测试我的代码是否正常工作。 I want to mock the database, more specifically the
db_conn
object since I don't want to use a real database (which would be a lot of work setting up the environment and maintaining it).我想模拟数据库,更具体地说是
db_conn
object,因为我不想使用真正的数据库(设置环境和维护它需要做很多工作)。
Any suggestions on how I can emulate the db_conn
?关于如何模拟
db_conn
的任何建议?
I've been exploring pytest-mock
and magicmock
but I don't think or know how to mock the db_conn
in my test.我一直在探索
pytest-mock
和magicmock
,但我不知道也不知道如何在我的测试中模拟db_conn
。
I believe you are right not testing cases on a real database because it's not unit testing anymore if you are using external dependencies.我相信你没有在真实数据库上测试用例是对的,因为如果你使用外部依赖项,它就不再是单元测试了。
There is a possibility to specify return-value
and customize it ( different return values on each iteration even ) for Mock
or MagicMock
objects.可以为
Mock
或MagicMock
对象指定return-value
并自定义它( 甚至每次迭代都有不同的返回值)。
from unittest.mock import Mock, patch
from app import db_conn
@patch('app.db_conn.find')
def test_some_func1(db_con_mock):
...
assert ...
Keep in mind that in each patch
you should specify the import path of db_conn
- the path where db_conn
**is used (I assume it's a different path in each test), not where it is defined.请记住,在每个
patch
中,您应该指定db_conn
的导入路径 -使用db_conn
** 的路径(我假设它在每个测试中都是不同的路径),而不是定义它的位置。
To answer the intial question "How to mock a imported object with pytest-mock or magicmock" you can do:要回答最初的问题“如何使用 pytest-mock 或 magicmock 模拟导入的 object”,您可以执行以下操作:
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(...)
note that you do not patch the actual source of db请注意,您没有修补db 的实际来源
For your other use case对于您的其他用例
I want to mock the database (using a mongo database), more specifically the "db_conn" object
我想模拟数据库(使用 mongo 数据库),更具体地说是“db_conn”object
you similarly follow the hints of the link above:您同样遵循上面链接的提示:
mock.patch('some_module1.db_conn', mock.MagicMock(return_value=...))
Given that, you will notice in your tests that db
from `db = db_conn.db_name2.db_collection2' will create another mock object.鉴于此,您会在测试中注意到来自 `
db
= db_conn.db_name2.db_collection2' 的 db 将创建另一个模拟 object。 Calls to that object will be recorded as well.对 object 的调用也将被记录。 In such a way you will be able to trace history of calls and values assignments as well.
通过这种方式,您还可以跟踪调用和值分配的历史记录。
Furthermore, see an example how to pach mongo db.此外,请参阅如何修补 mongo db 的示例。
For testing of Flask apps see the documentation of flask .有关 Flask 应用程序的测试,请参阅 flask的文档。 Also this is a nice explanation as well, and uses DB connections .
这也是一个很好的解释,并使用 DB 连接。
As a general hint, like @MikeMajara mentioned - separate your code more into smaller functions that are also easy to test.作为一般提示,就像@MikeMajara 提到的那样 - 将您的代码更多地分成更易于测试的更小的函数。 In tradition to TDD: write tests first, implement later, and refactor (especially DRY!)
TDD 的传统:先编写测试,后实施,然后重构(尤其是 DRY!)
Separation of concerns.关注点分离。 Build methods that do one, and only one thing.
构建只做一件事的方法。 Even more if you are going with TDD.
如果您要使用 TDD,那就更是如此。 In your example some_func2 does more than one.
在您的示例中, some_func2 不止一个。 You could refactor as follows:
你可以重构如下:
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)
With this approach you could easily test get_object_from_db
and check_condition_on_object
separately.使用这种方法,您可以轻松地分别测试
get_object_from_db
和check_condition_on_object
。 This will improve readability, avoid bugs, and help detecting these if they appear at some point.这将提高可读性,避免错误,并在它们出现时帮助检测它们。
About "mocking an imported object" .关于“模拟导入的对象” 。 You might be trying to mock an object with a library that is meant for a more advance case than yours.
您可能正在尝试使用一个用于比您的更高级案例的库来模拟 object。 These libraries provide you with a bunch of methods surrounding test environment out of the box that you might not need.
这些库为您提供了一堆您可能不需要的开箱即用的测试环境方法。 From the looks, you just want to populate an object with mock data, and/or interact with a mocked db_connection instance.
从外观上看,您只想用模拟数据填充 object,和/或与模拟的 db_connection 实例交互。 So...
所以...
To populate , I would simplify: You know the condition you want to test and you want to check if the result for a given object is the expected one.要填充,我会简化:您知道要测试的条件,并且要检查给定 object 的结果是否是预期的。 Just build yourself a
test_object_provider.py
that returns your known cases for true|false
.只需为自己构建一个
test_object_provider.py
,它会返回您已知的true|false
案例。 No need to make things more complex.没有必要让事情变得更复杂。
To use a fake MongoDB connection you can try with mongomock .要使用假的 MongoDB 连接,您可以尝试使用mongomock 。 (although ideally you would test mongo with a real instance in a separate test).
(尽管理想情况下您会在单独的测试中使用真实实例测试 mongo)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.