最近在尝试将bazel应用到python项目中,在将bazel test与python的测试对接的时候,发现了一些有趣的问题:
bazel test可以直接对接unittest框架,但是对接pytest框架时需要做一些改造。这篇文章就是来探究背后可能的原因的
1.pytest.main()与unittest.main()有和不同?
我们来进行如下的实验,同时在main上下去打印一下东西,看看输出结果:
Pytest
代码如下所示:
if __name__ == '__main__': print("########") pytest.main([__file__]) print("########") |
输出如下所示
######## =========================================================== test session starts =========================================================== platform darwin -- Python 3.6.8, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 rootdir: /Users/hexu/tsingj/caculator_bazel_python_demo plugins: remotedata-0.3.2, mock-3.6.1, doctestplus-0.1.3, arraydiff-0.2, openfiles-0.3.0 collected 1 item test/test_operators.py . [100%] ============================================================ 1 passed in 0.01s ============================================================ ######## |
Unittest
if __name__ == '__main__': print("########") unittest.main([__file__]) print("########") |
输出如下所示:
######## ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK |
总结:
由上可知,
- Pytest.main()执行后,python进程不会退出,因此可以打印两行日志
- Unittest.main()执行后,python进程会直接退出,因此只打印一行日志。
Unittest.main()的原理
以下摘自unittest的官方文档(unittest — Unit testing framework — Python 3.9.7 documentation)
The testRunner argument can either be a test runner class or an already created instance of it. By default main calls sys.exit() with an exit code indicating success or failure of the tests run. |
大意就是执行完所有case后,会调用sys.exit结束进程。
那sys.exit是怎么退出将程序的呢?
--- sys.exit(n) 退出程序引发SystemExit异常, 可以捕获异常执行些清理工作. n默认值为0, 表示正常退出. 其他都是非正常退出. 还可以sys.exit("sorry, goodbye!"); 一般主程序中使用此退出.
我们再回过头来看,pytest可以使用bazel的方式是怎么样的?之前stackoverflow推荐的方式有两种:
- sys.exit(pytest.main([__file__]))
- raise SystemExit(pytest.main([__file__]))
这样看来这两种写法其实是统一的。
我们将pytest的程序改为如下所示,那么bazel是否能够运行呢?
如图所示,bazel test也是能正常运行的。
看来,bazel在对接python框架时,希望执行完所有test case以后,能后直接捕获一个SystemExit异常,退出程序。
Pytest.main()的原理
那么,执行完pytest,main(),它的返回值是什么呢?我们先看一下官方文档:
它预期会返回一个exit code。我们来做一下实验:
if __name__ == '__main__': res = pytest.main([__file__]) print(type(res)) print(res) |
日志如下所示:
<enum 'ExitCode'> ExitCode.OK |
由此看来,它确实没有调用sys.exit或者直接抛出SystemExit异常,而是返回了一个退出吗。那么,ExitCode又是啥?可以参照以下两篇文章:
猜测:
Bazel test在对接python测试框架时,会通过捕获SystemExit异常来结束测试。如果没有捕获到,则无法进行测试。