How to mock a imported object with pytest-mock or magicmock

孤人 提交于 2019-12-05 06:09:52
Max V

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.

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.

To answer the intial question "How to mock a imported object with pytest-mock or magicmock" you can do:

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

For your other use case

I want to mock the database (using a mongo database), more specifically the "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. Calls to that object will be recorded as well. 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.

For testing of Flask apps see the documentation of flask. Also this is a nice explanation as well, and uses DB connections.

As a general hint, like @MikeMajara mentioned - separate your code more into smaller functions that are also easy to test. In tradition to TDD: write tests first, implement later, and refactor (especially DRY!)

Separation of concerns. Build methods that do one, and only one thing. Even more if you are going with TDD. In your example some_func2 does more than one. 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. 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. 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. 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. Just build yourself a test_object_provider.py that returns your known cases for true|false. No need to make things more complex.

To use a fake MongoDB connection you can try with mongomock. (although ideally you would test mongo with a real instance in a separate test).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!