在对象内部模拟模块调用

Mocking module call inside object

我有以下代码

@mock.patch('src.sql_connector.SqlWorkflowConnector')
def mock_workflow_connector(mock_connector):
    mock_connector.return_value.get_connection.return_value = MagicMock()
    mock_connector.return_value.get_cursor.return_value = MagicMock()
    return mock_connector

@mock.patch('src.sql_connector.SqlOverlordConnector')
def mock_overlord_connector(mock_connector):
    mock_connector.return_value.get_connection.return_value = MagicMock()
    mock_connector.return_value.get_cursor.return_value = MagicMock()
    return mock_connector

@mock.patch('src.sql_scrapper.SqlScrapper')
def test_retrieve_metadata(mock_sql_scrapper):
    sql_workflow_connector = mock_workflow_connector()
    sql_overlord_connector = mock_overlord_connector()
    scrapper = SqlScrapper(sql_workflow_connector, sql_overlord_connector)
    data = scrapper.retrieve_metadata("test_sample", "test_id", "test_status")
    print(data)

在我的 test_retrieve_meta_data 函数中,我有以下行

data = scrapper.retrieve_metadata("test_sample", "test_id", "test_status")

这会调用 cursor.execute() 调用

我得到的return值是

<MagicMock name='mock.cursor.execute()' id='4471052104'>
{'sample_name': <MagicMock name='mock.cursor.execute().SampleName' id='4471062824'>}

我希望能够修复这个 return 值,以便我可以继续测试它

我该如何更改 mock.cursor.execute().SampleName 调用 return 例如 "test_sample"?

首先,我怀疑您当前的模拟使用有误。您正在修补 SqlWorkflowConnectorSqlOverlordConnector classes,因此 return mock_connector 将 return 模拟 classsql_workflow_connector/sql_overlord_connector 都是 classes 的模拟。你的 SqlScrapper 真的期望构造函数中有 classes 吗?它们很可能应该是实例:

scrapper = SqlScrapper(sql_workflow_connector(), sql_overlord_connector())

为了避免歧义,我通常用 _cls/_instance 后缀命名模拟函数,这样我总能记得 returned 模拟函数替换了什么。例如,

def mock_workflow_connector_cls(mock_connector):
    ...

其次,回答您的实际问题:只需扩展您已有的模拟对象。示例:

@mock.patch('spam.SqlWorkflowConnector')
def mock_workflow_connector(mock_connector):
    mock_connector.return_value.get_connection.return_value = MagicMock()
    mocked_cursor = MagicMock()
    mocked_cursor.execute.return_value.SampleName = 'test_sample'
    mock_connector.return_value.get_cursor.return_value = mocked_cursor
    return mock_connector

根据 hoeflings 的反应,我能够修改并做出正确的调整。上面提供的示例没有按预期工作。

解决方法如下

@mock.patch('src.sql_connector.SqlWorkflowConnector')
def mock_workflow_connector(mock_connector):
    mock_connector.return_value.get_connection.return_value = MagicMock()
    mocked_cursor = MagicMock()
    mocked_cursor.execute.return_value.SampleName = 'test_sample_name'
    mocked_cursor.execute.return_value.ClientSampleId = 'test_client_sample_id'
    mocked_cursor.execute.return_value.ClientSubjectId = 'test_client_subject_id'
    mocked_cursor.execute.return_value.ClientName = 'test_client_name'
    mocked_cursor.execute.return_value.ProjectId = 'test_project_id'
    mocked_cursor.execute.return_value.ProjectName= 'test_project_name'
    mocked_cursor.execute.return_value.WorkflowExecutionId = 12345
    mock_connector.return_value.get_cursor.return_value = mocked_cursor
    print(mock_connector.return_value.get_cursor.return_value)
    mock_connector.cursor = mocked_cursor
    return mock_connector

@mock.patch('src.sql_connector.SqlOverlordConnector')
def mock_overlord_connector(mock_connector):
    mock_connector.return_value.get_connection.return_value = MagicMock()
    mock_connector.return_value.get_cursor.return_value = MagicMock()
    return mock_connector

def test_retrieve_metadata(t_row):
    sql_workflow_connector = mock_workflow_connector()
    sql_overlord_connector = mock_overlord_connector()
    scrapper = SqlScrapper(sql_workflow_connector, sql_overlord_connector)
    data = scrapper.retrieve_metadata("test_sample", "test_id", "test_status")
    print(data)
    print(t_row)
    assert data == t_row