Pytest 执行特定功能但不执行 main()

Pytest execute specific functions but not main()

我正在完成 udemy 的 python 编码课程,并且有一个 project 目录结构如下:

├── LICENSE
├── README.md
├── __init__.py
├── requirements.txt
├── src
│   ├── __init__.py
│   └── milage_converter.py
└── tests
    ├── __init__.py
    └── milage_converter_test.py

milage_converter_test.py 内,我正在 运行 进行一些简单的测试,例如:

from py_exercises.src import milage_converter


def test_base_milage_string_to_float(monkeypatch):
    # monkeypatch the "input" function, so that it returns "10".
    monkeypatch.setattr('builtins.input', lambda _: "10")
    assert milage_converter.get_base_milage(1) == 10.0

但是这些似乎在调用 main() function in milage_converter.py

def get_conversion_direction():
    """Gets the conversion direction input from a user, converts to a float and returns."""
    user_input = input(
        "Would you like to convert from (1) Miles to Kilometers or (2) Kilometers to Miles: ")
    try:
        user_input = float(user_input)
    except ValueError:
        print(
            "Invalid input, options are 1 for Miles to Kilometer or 2 for Kilometers to Miles.")
        get_conversion_direction()
    while user_input in (1, 2):
        user_input = input(
            "You chose an invalid value, please enter a value of 1 or 2: ")
    return user_input


def get_base_milage(unit):
    """Gets the milage input from a user, converts to a float and returns."""
    in_milage = input(f"Please input a value in {unit} to convert: ")
    try:
        in_milage = float(in_milage)
    except ValueError:
        print(
            "Invalid input, please input a valid numerical value to convert.")
        # Adding return avoids the exception being returned:
        # 
        return get_base_milage(unit)
    return in_milage


def main():
    direction = get_conversion_direction()
    if direction == 1:
        print(
            f"You have selected {direction}, converting from Miles to Kilometers.")
        miles = int(get_base_milage("Miles"))
        print(f"{miles} Miles is equal to {round(miles * 1.609344, 2)}")
    else:
        print(
            f"You have selected {direction}, converting from Kilometers to Miles.")
        kilometers = get_base_milage("Kilometers")
        print(f"{kilometers} Kilometers is equal to {round(kilometers / 1.609344, 2)}")


main()

当我从项目目录 运行 pytest 时,我得到输入提示,就好像我有 运行 milage_converter.py 模块一样 python3.9 milage_converter.py。这是抛出 pytest 无法捕获标准输入的错误。

------------------------------------------------------------------------------------------ Captured stdout -------------------------------------------------------------------------------------------
Would you like to convert from (1) Miles to Kilometers or (2) Kilometers to Miles: 
====================================================================================== short test summary info =======================================================================================
ERROR tests/milage_converter_test.py - OSError: pytest: reading from stdin while output is captured!  Consider using `-s`.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================================================================== 1 error in 0.10s ==========================================================================================

我期望 pytest 会简单地调用指定的函数,get_base_milage() 并模拟输入。如果我在 milage_converter.py 的第 51 行注释掉 main() pytest 会像我预期的那样 运行 ,所以我觉得我在这里遗漏了一些东西 pytest/mocking python.

如果您打算 运行 将 milage_converter.py 作为脚本,例如python milage_converter.py,那么最好检查它是否如记录的那样 __main__

A module can discover whether or not it is running in the main scope by checking its own name, which allows a common idiom for conditionally executing code in a module when it is run as a script or with python -m but not when it is imported:

if __name__ == "__main__":
    # execute only if run as a script
    main()

因此将此添加到您的 milage_converter.py

...
if __name__ == '__main__':
    main()

你的测试失败的原因是当你通过from py_exercises.src import milage_converter导入文件时(此时还没有活动补丁),它当然会读取文件中的代码。由于您对 main() 的调用就在那里,因此它会立即执行。现在有了这个条件,它将是 False 因此不会再执行它了。

相关参考:

  • What does if __name__ == "__main__": do?