首页 > 解决方案 > 执行 python 脚本,获取其打印和日志输出并断言 unittest

问题描述

编辑:感谢@eemz 重新设计结构和使用的想法from unittest.mock import patch,但问题仍然存在。

所以我最近偶然发现了 unittest 并且我有一个通常像这样开始的程序python run.py -config /path/to/config.file -y。我想在一个单独的test.py文件中编写一个简单的测试:执行脚本,传递提到的参数并获取它的所有输出。我传递了一个缺少某些东西的准备好的配置文件,因此run.py它将中断并使用以下命令准确记录此错误logging.error:“xyz 在配置文件中丢失!” (见下面的例子)。我会从中得到几句话print(),然后logging实例启动并从那里开始处理。我如何获得它的输出以便我可以检查它?随意重写这个,因为我还在学习,请多多包涵。

简化示例:

run.py

import logging

def run(args):
  < args.config = /path/to/config.file >
  cnfg = Config(args.config)

  cnfg.logger.info("Let's start with the rest of the code!") # This is NOT in 'output' of the unittest
  < code >

if __name__ == "__main__":
  print("Welcome! Starting execution.") # This is in 'output' of the unittest
  < code to parse arguments 'args' >
  run(args)

Config.py

import logging

class Config:
  def __init__(self):
    print("Creating logging instance, hold on ...") # This is in 'output' of the unittest
    logger = logging.getLogger(__name__)
    console_handler = logging.StreamHandler()
    logger.addHandler(console_handler)
    logger.info("Logging activated, let's go!") # This is NOT in 'output' of the unittest
    self.logger = logger

    if xyz not in config:
      self.logger.error("xyz was missing in Config file!") # This is NOT in 'output' of the unittest
      exit(1)

test.py

import unittest
from unittest.mock import patch

class TestConfigs(unittest.TestCase):
    def test_xyz(self):
        with patch('sys.stdout', new=StringIO()) as capture:
            with self.assertRaises(SystemExit) as cm:
                run("/p/to/f/missing/xyz/f", "", False, True)
        output = capture.getvalue().strip()

        self.assertEqual(cm.exception.code, 1)
        # Following is working, because the print messages are in output
        self.assertTrue("Welcome! Starting execution." in output)
        # Following is NOT working, because the logging messages are not in output
        self.assertTrue("xyz was missing in Config file!" in output)

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

标签: pythonunit-testingtestingloggingpython-unittest

解决方案


我会像这样重组run.py:

import logging

def main():
  print("Welcome! Starting execution.")
  Etc etc

if __name__ == "__main__":
  main()

然后,您可以在单元测试中调用函数 run.main() 而不是分叉子进程。

from io import StringIO
from unittest.mock import patch
import sys

import run

class etc etc
  def test_run etc etc:
    with patch('sys.stdout', new=StringIO()) as capture:
      sys.argv = [‘run.py’, ‘-flag’, ‘-flag’, ‘-flag’]
      run.main()
      output = capture.getvalue().strip()
    assert output == <whatever you expect it to be>

如果您不熟悉单元测试,那么您之前可能没有见过模拟。实际上,我正在用假的标准输出替换标准输出,以捕获发送到那里的所有内容,以便稍后将其拉出到变量输出中。

事实上,围绕 sys.argv 的第二个补丁会更好,因为我在这里所做的,对真实 argv 的分配,实际上会改变它,这将影响同一文件中的后续测试。


推荐阅读