有没有办法使用 mock 在 pytest 中模拟完整的代码?

Is there a way to mock a complete bit of code in pytest using mock?

比如每次测试发现

database.db.session.using_bind("reader")

我想删除 using_bind("reader")) 并只使用

database.db.session

使用 mocker

conftest.py

中尝试过这样使用它
@pytest.fixture(scope='function')
def session(mocker):
    mocker.patch('store.database.db.session.using_bind', return_value=_db.db.session)

但到目前为止没有任何效果。

被测代码:

from store import database     
results = database.db.session.using_bind("reader").query(database.Order.id).join(database.Shop).filter(database.Shop.deleted == False).all(), 

然后我得到

AttributeError: 'scoped_session' object has no attribute 'using_bind' as an error.

让我们从 MRE 开始,其中被测代码使用假数据库:

from unittest.mock import Mock, patch

class Session:
    def using_bind(self, bind):
        raise NotImplementedError(f"Can't bind {bind}")

    def query(self):
        return "success!"


database = Mock()
database.db.session = Session()


def code_under_test():
    return database.db.session.using_bind("reader").query()


def test():
    assert code_under_test() == "success!"

运行 此测试失败:

E       NotImplementedError: Can't bind reader

所以我们想在 code_under_test 中模拟 session.using_bind 以便它 returns session -- 这将使我们的测试通过。

我们使用 patch 来做到这一点,就像这样:

@patch("test.database.db.session.using_bind")
def test(mock_bind):
    mock_bind.return_value = database.db.session
    assert code_under_test() == "success!"

请注意,我的代码位于一个名为 test.py 的文件中,因此我的 patch 调用适用于 test 模块——您需要调整它以指向该模块在您自己的代码中进行测试。

另请注意,在调用被测代码之前,我需要设置我的模拟