Тестирование функции с @ cache.memoize дает PicklingError - PullRequest
0 голосов
/ 04 февраля 2020

У меня есть несколько тестов для функций, которые используют кэш, например:

Функция:

@retry(stop=stop_after_attempt(3))
@cache.cached(timeout=60, key_prefix='resouce_group_list')
def get_azure_resource_groups():

    data = []
    resource_client = get_azure_resource_client()
    for item in resource_client.resource_groups.list():
        data.append(item)
    return data

Тест:

@patch("dev_maintenance.machines.get_azure_resource_client")
def test_get_azure_list_rg(get_azure_resource_client):

    cache.clear()
    data = []
    with app.app_context():
        ret = get_azure_resource_groups()
        get_azure_resource_client.assert_called_once()
        expected = get_azure_resource_client.return_value.resource_groups.list.return_value
        assert len(get_azure_resource_client.return_value.method_calls) == 1
        for item in expected:
            data.append(item)
        assert ret == data
        cache.clear()

Вышеприведенный тест работает нормально, он проходит, ошибок нет, и тест использует кеш.

Но у меня есть другие тесты, и декоратор здесь не имеет значения, он выдаст ту же ошибку, если я изменю декоратор на @cache.cache:

Функция:

@retry(stop=stop_after_attempt(3))
@cache.memoize(60)
def get_azure_machine_info(rg_name, machine_name, expand="instanceView"):

    try:
        compute_client = get_azure_compute_client()
        return compute_client.virtual_machines.get(rg_name, machine_name, expand=expand)
    except CloudError:
        return None

Тест:

@patch("dev_maintenance.machines.get_azure_compute_client")
def test_get_azure_machine_info (get_azure_compute_client):

    cache.delete_memoized(get_azure_machine_info)
    with app.app_context():
        ret = get_azure_machine_info("rg1", "m1")
        print(ret)
        get_azure_compute_client.assert_called_once()
        assert len(get_azure_compute_client.return_value.method_calls) == 1
        assert (
            ret == get_azure_compute_client.return_value.virtual_machines.get.return_value
            )

        get_azure_compute_client.return_value.virtual_machines.get.assert_called_once_with(
            "rg1", "m1", expand="instanceView"
            )
        cache.delete_memoized(get_azure_machine_info)

Теперь здесь тест не пройден с ошибкой в ​​этой строке ret = get_azure_machine_info("rg1", "m1"):

value = None, from_value = PicklingError("Can't pickle <class 'unittest.mock.MagicMock'>: it's not the same object as unittest.mock.MagicMock")

>   ???
E   tenacity.RetryError: RetryError[<Future at 0x105c7c3d0 state=finished raised PicklingError>]

<string>:3: RetryError

Я попытался смоделировать кеш, передавая оформитель патча, например:

@patch("dev_maintenance.machines.cache") or @patch("dev_maintenance.cache")

Я попытался установить для CACHE_TYPE значение null в тестовом примере, создать экземпляр объекта кэша и передать конфигурацию:

cache = Cache()
cache.init_app(app, config={"CACHE_TYPE": "redis"})

но безуспешно, какая-нибудь помощь?

Ответы [ 2 ]

0 голосов
/ 14 февраля 2020

Я пишу здесь ответ для людей, которым нужно протестировать функции, которые кэшируются с flask -эшированием и имеют ту же ошибку, что и я.

Мне нужно было создать объект внутри протестируйте и сделайте mock_value = Object следующим образом:

Сначала я создаю простой класс:

class MachineInfo(object):
    pass

Затем в моем тесте:

@patch("dev_maintenance.machines.get_azure_compute_client")
def test_get_azure_machine_info (get_azure_compute_client):

    cache.clear()
    expected_res = MachineInfo()
    expected_res.id = "id"
    expected_res.name = "machine1"
    expected_res.location = "location"
    expected_res.hardware_profile = "hardware"
    expected_res.storage_profile = "storage"
    expected_res.network_profile = "network_profile"
    get_azure_compute_client.return_value.virtual_machines.get.return_value = expected_res
    res = get_azure_machine_info("rg1", "m1")
    assert res == expected_res
    cache.clear()

Тогда я мог бы утверждать function_call() == Object или function_call() == mock.return_value

Это имитирует то, что возвращает фактический azure, объект, поэтому я просто заставляю макет возвращать объект, который я создал, чтобы я мог смоделировать саму функцию.

0 голосов
/ 04 февраля 2020

Это ссылка на старый ответ, но я думаю, что обычно объекты MagicMock не предназначены для травления: https://github.com/thadeusb/flask-cache/issues/52

Это сообщение об ошибке отличается, и это больше похоже на то, что вы видите: Есть ли способ заставить python рассол игнорировать ошибки "это не тот же объект"

Возможно вы могли бы замените префикс домена на класс, как ответ выше, но я не уверен, что он преодолеет другие трудности выбора класса MagicMock:

`@patch("__main__.get_azure_compute_client")`
...