在 Python 中,pytest
是一个强大的测试框架,用于编写和运行测试用例。通常我们会在命令行中运行 pytest
,但是有时你可能希望从模块或脚本的内部运行 pytest
,比如为了自动化测试或集成到某个工作流程中。
1、问题背景
当你从模块内部运行 Pytest 时,Pytest 似乎会缓存测试。对模块或测试的任何更改都会被忽略。由于在 iPad 上编写 Python,因此无法从命令行运行 Pytest,只能使用 pytest.main() 来运行测试。这个问题已经广泛搜索,但只能找到一个相似的问题,建议从命令行运行 Pytest。
2、解决方案
Pytest 不会缓存任何内容。每个 Python 解释器实例仅读取一个文件一次。虽然有一个内置的 reload,但它几乎从不做你希望它做的事情。因此,如果运行以下代码:
import pytest
...
while True:
import my_nifty_app
my_nifty_app.be_nifty()
pytest.main()
即使 my_nifty_app.py 在磁盘上发生更改,它也将被只读取一次。实际上需要的是类似这样的代码:
exit_code = pytest.main()
sys.exit(exit_code)
这将结束解释器的该实例,这是确保重新读取源文件唯一的方法。
代码例子:
import pytest
# 定义测试函数
def test_function():
assert True
# 定义一个包含测试函数的模块
module = """
def test_function():
assert True
"""
# 从模块内部运行 Pytest
exec(module)
pytest.main()
# 对模块进行更改
module = """
def test_function():
assert False
"""
# 再次从模块内部运行 Pytest
exec(module)
pytest.main()
# 现在,测试应该失败
要从模块内部运行 pytest
,可以使用 pytest.main()
方法。这是一个 Python 接口,允许你以编程方式运行测试。
步骤:
-
安装
pytest
:
确保已安装pytest
。如果没有安装,可以通过以下命令安装:pip install pytest
-
从模块内部调用
pytest.main()
:通过调用
pytest.main()
,你可以从 Python 脚本中运行测试。你可以传递命令行选项给pytest.main()
来控制运行的测试文件或参数。示例 1:从模块内部运行
pytest
创建一个简单的测试文件
test_sample.py
,并编写测试用例:# test_sample.py def test_addition(): assert 1 + 1 == 2 def test_subtraction(): assert 2 - 1 == 1
然后在另一个模块或脚本中运行
pytest.main()
来执行测试:# run_tests.py import pytest def run_tests(): # 运行所有测试 pytest.main() if __name__ == "__main__": run_tests()
当你运行
run_tests.py
时,它会从内部执行pytest
,并自动运行当前目录下的所有测试文件(以test_
开头或以_test
结尾的文件)。python run_tests.py
输出将显示测试结果,如同你从命令行运行
pytest
一样。 -
指定测试文件或目录:
你可以通过传递参数来指定要运行的测试文件或目录。例如,指定某个特定的测试文件运行:
# run_tests.py import pytest def run_tests(): # 只运行指定的测试文件 pytest.main(["test_sample.py"]) if __name__ == "__main__": run_tests()
-
传递 pytest 选项:
pytest.main()
支持接受命令行选项作为参数。例如,如果你想启用详细模式(-v
)或者只显示失败信息(--maxfail=1
),可以传递这些选项。# run_tests.py import pytest def run_tests(): # 使用命令行选项运行测试:启用详细模式并设置最大失败次数 pytest.main(["-v", "--maxfail=1", "test_sample.py"]) if __name__ == "__main__": run_tests()
-
处理
pytest.main()
返回值:pytest.main()
返回一个整数,表示测试运行的结果:- 0:所有测试都通过。
- 1:有测试失败。
- 2:测试执行被中断。
- 3:内部错误。
- 4:命令行用法错误。
你可以根据这个返回值做进一步的处理:
# run_tests.py import pytest def run_tests(): result = pytest.main(["-v", "test_sample.py"]) if result == 0: print("All tests passed.") else: print(f"Tests failed with code {result}") if __name__ == "__main__": run_tests()
完整示例
以下是一个完整的示例,展示了如何从模块内部运行 pytest
,并传递自定义参数:
# run_tests.py
import pytest
def run_tests():
# 运行测试,启用详细模式,并且指定只运行 test_sample.py
result = pytest.main(["-v", "test_sample.py"])
if result == 0:
print("All tests passed.")
else:
print(f"Tests failed with code {result}")
if __name__ == "__main__":
run_tests()
其他注意事项
- 避免递归调用:当从模块内部运行
pytest
时,要避免直接在测试文件中调用pytest.main()
,否则可能导致递归调用,因为pytest
运行时也会加载测试文件。 - 虚拟环境和依赖管理:确保在正确的虚拟环境中运行
pytest
,以避免依赖冲突。
通过这些步骤,你可以在 Python 脚本中方便地调用和控制 pytest
,从而实现自动化测试或集成测试的需求。