You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用runpy.run_module测试Python包__main__.py的退出码?

可以用runpy.run_module获取退出码,方法如下

当然可以!runpy.run_module是测试这类模块的合适工具,不过它不会直接返回退出码——因为当你的__main__.py调用sys.exit()时,runpy会把这个退出操作转化为SystemExit异常抛出,而不是真的终止整个测试进程。我们只需要捕获这个异常,就能拿到设置的退出码。

核心思路

  1. 调用runpy.run_module时,指定run_name='__main__',让模块以主程序模式运行(触发if __name__ == "__main__"分支)。
  2. 捕获模块执行时抛出的SystemExit异常,通过异常对象的code属性获取退出码。
  3. 结合mock工具(比如unittest.mock)模拟你已测试过的函数的返回值,验证不同场景下的退出码是否符合预期。

示例代码

假设你的包结构是这样的:

my_package/
├── __init__.py
├── __main__.py
└── core.py  # 包含已测试的核心函数

你的__main__.py可能长这样:

import sys
from .core import core_function

def main():
    result = core_function()
    # 根据核心函数的结果设置退出码
    sys.exit(0 if result else 1)

if __name__ == "__main__":
    main()

对应的单元测试(用unittest框架)可以这么写:

import unittest
import runpy
from unittest.mock import patch

class TestMainModuleExitCodes(unittest.TestCase):
    def test_exit_code_on_success(self):
        # 模拟核心函数返回成功状态
        with patch("my_package.core.core_function", return_value=True):
            try:
                # 运行__main__模块
                runpy.run_module("my_package", run_name="__main__")
            except SystemExit as exit_exc:
                # 验证退出码是否为0
                self.assertEqual(exit_exc.code, 0)
            else:
                # 如果没抛出SystemExit,说明模块没有正确调用sys.exit
                self.fail("Expected SystemExit to be raised by __main__.py")

    def test_exit_code_on_failure(self):
        # 模拟核心函数返回失败状态
        with patch("my_package.core.core_function", return_value=False):
            try:
                runpy.run_module("my_package", run_name="__main__")
            except SystemExit as exit_exc:
                self.assertEqual(exit_exc.code, 1)
            else:
                self.fail("Expected SystemExit to be raised by __main__.py")

if __name__ == "__main__":
    unittest.main()

关键细节说明

  • run_name='__main__'是必须的:如果不指定这个参数,模块的__name__会被设为'runpy',不会触发if __name__ == "__main__"的执行逻辑。
  • 处理SystemExit的无参数情况:如果你的模块里调用了sys.exit()(不带参数),exit_exc.code会是None,这时候你需要调整断言逻辑(比如断言exit_exc.code is None或者默认视为0)。
  • 备选方案:如果你更倾向于模拟真实命令行调用的场景,也可以用subprocess.run执行python -m my_package,然后通过subprocess.CompletedProcess.returncode获取退出码。不过runpy的优势是在同一个进程内运行,测试速度更快,也更容易mock内部函数。

内容的提问来源于stack exchange,提问作者Curtis Pew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:45:12