【问题标题】:Mocking return value of a nested call in Python mock library在 Python 模拟库中模拟嵌套调用的返回值
【发布时间】:2019-07-10 13:30:42
【问题描述】:

这个库是全新的

这是我的模拟对象的调用堆栈

[call(),
 call('test'),
 call().instance('test'),
 call().instance().database('test'),
 call().instance().database().snapshot(),
 call().instance().database().snapshot().__enter__(),
 call().instance().database().snapshot().__enter__().execute_sql('SELECT * FROM users'),
 call().instance().database().snapshot().__exit__(None, None, None),
 call().instance().database().snapshot().__enter__().execute_sql().__iter__()]

这是我使用的代码

    @mock.patch('testmodule.Client')
    def test_read_with_query(self, mock_client):
        mock = mock_client()
        pipeline = TestPipeline()
        records = pipeline | ReadFromSpanner(TEST_PROJECT_ID, TEST_INSTANCE_ID, self.database_id).with_query('SELECT * FROM users')
        pipeline.run()
        print mock_client.mock_calls
        exit()

我想模拟整个堆栈,最终它会给我一些假数据,我将提供这些数据作为返回值。

被测试的代码是


        spanner_client = Client(self.project_id)
        instance = spanner_client.instance(self.instance_id)
        database = instance.database(self.database_id)

        with database.snapshot() as snapshot:
            results = snapshot.execute_sql(self.query)

所以我的要求是results 变量应该包含我将提供的数据。

如何为这样的嵌套调用提供返回值

谢谢

【问题讨论】:

    标签: python unit-testing python-mock


    【解决方案1】:

    在被测代码中为instancedatabasesnapshot 对象创建单独的MagicMock 实例。使用return_value 配置每个方法的返回值。这是一个例子。我将测试中的方法简化为一个名为mut 的独立函数。

    # test_module.py : the module under test
    class Client:
        pass
    
    def mut(project_id, instance_id, database_id, query):
        spanner_client = Client(project_id)
        instance = spanner_client.instance(instance_id)
        database = instance.database(database_id)
    
        with database.snapshot() as snapshot:
            results = snapshot.execute_sql(query)
            return results
    
    # test code (pytest)
    from unittest.mock import MagicMock
    from unittest import mock
    
    from test_module import mut
    
    @mock.patch('test_module.Client')
    def test_read_with_query(mock_client_class):
        mock_client = MagicMock()
        mock_instance = MagicMock()
        mock_database = MagicMock()
        mock_snapshot = MagicMock()
        expected = 'fake query results'
    
        mock_client_class.return_value = mock_client
        mock_client.instance.return_value = mock_instance
        mock_instance.database.return_value = mock_database
        mock_database.snapshot.return_value = mock_snapshot
        mock_snapshot.execute_sql.return_value = expected
        mock_snapshot.__enter__.return_value = mock_snapshot
    
        observed = mut(29, 42, 77, 'select *')
    
        mock_client_class.assert_called_once_with(29)
        mock_client.instance.assert_called_once_with(42)
        mock_instance.database.assert_called_once_with(77)
        mock_database.snapshot.assert_called_once_with()
        mock_snapshot.__enter__.assert_called_once_with()
        mock_snapshot.execute_sql.assert_called_once_with('select *')
        assert observed == expected
    

    这个测试有点笨拙。考虑通过使用夹具和设置模拟的before 函数将其分开。

    【讨论】:

      【解决方案2】:

      要么将值直接设置为您的 Mock 实例(那些 entersexit 应该没有看到):

      mock.return_value.instance.return_value.database.return_value.snapshot.return_value.execute_sql.return_value = MY_MOCKED_DATA
      

      或修补并将 return_value 设置为目标方法,例如:

      @mock.patch('database_engine.execute_sql', return_value=MY_MOCKED_DATA)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        • 2015-09-09
        • 1970-01-01
        • 2020-04-06
        相关资源
        最近更新 更多