【问题标题】:async-aware mock/override environment variables?异步感知模拟/覆盖环境变量?
【发布时间】:2022-02-14 01:56:04
【问题描述】:

是否有一种异步感知方式来模拟环境变量?

我想要的是一个上下文管理器,它可以让我模拟 with 块内的代码,以便在执行该代码时应用模拟,但在执行其他代码时不应用模拟。

类似这样,但通过了:

import pytest
import os
import asyncio
import mock


async def aaa():
    with mock.patch.dict(os.environ, {"FAVORITE_LETTER": "A"}, clear=True):
        for _ in range(50):
            assert os.environ["FAVORITE_LETTER"] == "A"
            await asyncio.sleep(0)


async def bbb():
    with mock.patch.dict(os.environ, {"FAVORITE_LETTER": "B"}, clear=True):
        for _ in range(50):
            assert os.environ["FAVORITE_LETTER"] == "B"
            await asyncio.sleep(0)


@pytest.mark.asyncio
async def test_mock_env():
    await asyncio.gather(aaa(), bbb())

目前失败:

    async def aaa():
        with mock.patch.dict(os.environ, {"FAVORITE_LETTER": "A"}, clear=True):
            for _ in range(50):
>               assert os.environ["FAVORITE_LETTER"] == "A"
E               AssertionError: assert 'B' == 'A'
E                 - A
E                 + B

test_mock_env.py:9: AssertionError

=======

编辑:

接受的答案解决了我提出的问题,但没有解决我的问题,因为我在制作示例时简化了太多。我的真正问题不会成为粘贴在这里的好例子,但这更接近我的实际情况:

from my_library import some_component_i_control
from someone_elses_library import not_my_code

async def wrap_not_my_code():
    with patch.dict(os.environ, {"ENV_VARS_NEEDED_FOR_COMPONENT_I_DONT_CONTROL": "A"}, clear=True):
        await not_my_code()  # this runs forever but does not block the event loop


@pytest.mark.asyncio
async def test_mock_env():
    task_for_not_my_code = asyncio.create_task(wrap_not_my_code())
    task_for_my_component = asyncio.create_task(some_component_i_control())
    await asyncio.wait([task_for_my_component, task_for_not_my_code], return_when=asyncio.FIRST_COMPLETED) 
    # ...
    # Make some assertions about the results

我正在尝试测试我控制的库与我不控制的库之间的交互,我无法控制的组件看到环境的修改版本。

实际上更糟糕的是:我需要多个我无法控制的组件的任务实例,它们都可以看到不同的环境,并且都与我自己的代码同时运行。这可能吗?

【问题讨论】:

  • 您的测试是同时运行的,正如您所见,这导致了数据竞争问题,为什么不按顺序运行它们
  • 我的实际用例需要并发——这里的例子不是真实的,所以不需要并发。

标签: python async-await mocking


【解决方案1】:

您将需要一些东西来允许对状态的独占访问,在这种情况下,asyncio.Lock 对象可以工作。这就需要你把原来的代码修改成如下。

import pytest
import os
import asyncio
from unittest.mock import patch


async def aaa(lock):
    for _ in range(50):
        async with lock:
            with patch.dict(os.environ, {"FAVORITE_LETTER": "A"}, clear=True):
                assert os.environ["FAVORITE_LETTER"] == "A"
        await asyncio.sleep(0)


async def bbb(lock):
    for _ in range(50):
        async with lock:
            with patch.dict(os.environ, {"FAVORITE_LETTER": "B"}, clear=True):
                assert os.environ["FAVORITE_LETTER"] == "B"
        await asyncio.sleep(0)


@pytest.mark.asyncio
async def test_mock_env():
    lock = asyncio.Lock()
    await asyncio.gather(aaa(lock), bbb(lock))

【讨论】:

  • 这消除了并发性,对吧?如果我愿意,我根本不会做任何这种异步,正如你在评论中所建议的那样。
  • 这根本不会消除并发性,两个函数同时运行它只是创建了修改环境变量的独占访问权
  • 对于我的用例,我确实需要每个协程 env var 保持设置,但是——在循环中一遍又一遍地设置/取消设置是行不通的。我很抱歉举了一个没有反映我情况的那个方面的例子。
  • 在这种情况下你能举个例子吗?我可以修改我的答案
  • 谢谢!我已经编辑了问题,试图更清楚地了解我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多