Ложное возвращаемое значение вложенного вызова в фиктивной библиотеке Python - PullRequest
0 голосов
/ 10 июля 2019

Совершенно новый для этой библиотеки

Вот стек вызовов моего смоделированного объекта

[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 переменная должна содержать данные, которые я предоставлю.

Как я могу предоставить возвращаемое значение для таких вложенных вызовов

Спасибо

Ответы [ 2 ]

1 голос
/ 10 июля 2019

Создайте отдельные MagicMock экземпляры для объектов instance, database и snapshot в тестируемом коде. Используйте 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, которая устанавливает макеты.

1 голос
/ 10 июля 2019

Либо установите значение непосредственно для вашего экземпляра Mock (те, которые входит и exit не должны были видеть) с помощью:

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)
...