【问题标题】:Dealing with MagicMock Conversions in Pytest在 Pytest 中处理 MagicMock 转换
【发布时间】:2021-09-24 08:36:05
【问题描述】:

上下文是我正在尝试对一些涉及正在检查的 Python 版本的 Python 代码进行单元测试。我在platform 方法下工作,但有人告诉我使用platform 不是一个好主意,我应该改用sys.version_info。因此我有以下逻辑:

def main() -> None:
  python_version = f"{sys.version_info[0]}.{sys.version_info[1]}"

  if float(python_version) < 3.7:
     sys.stderr.write("\nQuendor requires Python 3.7 or later.\n")
     sys.stderr.write(f"Your current version is {python_version}\n\n")
     sys.exit(1)

(我确实意识到我也可以为我的支票使用if sys.version_info &lt; (3, 7):。)

我的单元测试是这样的:

def test_bad_python_version(capsys) -> None:
    import sys
    from quendor.__main__ import main

    with mock.patch.object(sys, "version_info") as v_info:
        v_info.major = 3
        v_info.minor = 5

        main()

    terminal_text = capsys.readouterr()
    print(terminal_text)

当然,我的测试还没有完成。我正在尝试打印输出以确保测试正常工作。

运行该测试让我知道:

ValueError: could not convert string to float:
"<MagicMock name='version_info.__getitem__()'
id='1417904973808'>.<MagicMock name='version_info.__getitem__()'
id='1417904973808'>"

顺便说一句,如果我将 main 中的条件切换到此 (if sys.version_info &lt; (3, 7):),我会收到类似但不同的错误:

TypeError: '<' not supported between instances of 'MagicMock' and 'tuple'

我不确定如何正确传递 version_info,我认为这是我的问题。我已经搜索了文档,并且在这里看到了关于 MagicMock 的其他问题(例如:How to unit test a python version switch),但没有明确说明:“这就是如何做到这一点。”

我应该注意到,在使用 platform 时,我的版本运行良好。我的那个版本的测试看起来像这样:

with mock.patch.object(
        platform,
        "python_version",
        return_value="3.5",
    ), pytest.raises(SystemExit) as pytest_wrapped_e:
        main()

    terminal_text = capsys.readouterr()
    expect(terminal_text.err).to(contain("Quendor requires Python 3.7"))

关键似乎是我使用的“return_value”。

【问题讨论】:

    标签: python pytest python-unittest


    【解决方案1】:

    在您的单元测试中,您正在设置/模拟版本

    v_info.major = 3
    v_info.minor = 5
    

    但访问sys.version_info[0]sys.version_info[1]

    尝试将main()中的版本提取更改为

    python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
    

    为我工作:

    In [8]: with mock.patch.object(sys, "version_info") as v_info:
       ...:     v_info.major = 3
       ...:     v_info.minor = 5
       ...:     main()
       ...: 
    
    Quendor requires Python 3.7 or later.
    Your current version is 3.5
    

    【讨论】:

    • 就是这样!我并没有考虑更改我的实际代码,认为所有这些都必须是我最初的嘲笑方式的问题。非常感谢。这成功了!
    【解决方案2】:

    你正在这样修补它:

    with mock.patch.object(sys, "version_info") as v_info:
        v_info.major = 3
        v_info.minor = 5
    

    因此,它将应用于以下调用:

    sys.version_info.major  # Will display 3
    sys.version_info.minor  # Will display 5
    

    但是,相反,您在源代码中访问的内容不同:

    sys.version_info
    sys.version_info[0]
    sys.version_info[1]
    

    显然,您的补丁不会反映,因为它们针对的是.major.minor,但无论如何都不会调用它们。相反,将要应用的补丁更改为sys.version_info,它应该返回一个项目元组(3, 5),这样它就会反映出来,因为它是你源代码中调用的那个。无需从源代码中更改任何内容:

    src.py

    import sys
    
    
    def main() -> None:
        print("Call main")
        python_version = f"{sys.version_info[0]}.{sys.version_info[1]}"
    
        if float(python_version) < 3.7:
            print("Quendor requires Python 3.7 or later.")
    
    
    def main2() -> None:
        print("Call main2")
        if sys.version_info < (3, 7):
            print("Quendor requires Python 3.7 or later.")
    

    test_src.py

    import sys
    from unittest import mock
    
    from src import main, main2
    
    
    def test_bad_python_version():
        with mock.patch.object(sys, "version_info", (3, 5)) as v_info:
            main()
            main2()
    
    def test_good_python_version():
        with mock.patch.object(sys, "version_info", (3, 8)) as v_info:
            main()
            main2()
    

    输出

    $ pytest -q -rP
    ..                                                                                            [100%]
    ============================================== PASSES ===============================================
    ______________________________________ test_bad_python_version ______________________________________
    --------------------------------------- Captured stdout call ----------------------------------------
    Call main
    Quendor requires Python 3.7 or later.
    Call main2
    Quendor requires Python 3.7 or later.
    _____________________________________ test_good_python_version ______________________________________
    --------------------------------------- Captured stdout call ----------------------------------------
    Call main
    Call main2
    2 passed in 0.06s
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多