【问题标题】:How do I make PyCharm show code coverage of programs that use multiple processes?如何让 PyC​​harm 显示使用多个进程的程序的代码覆盖率?
【发布时间】:2014-07-29 23:37:01
【问题描述】:

假设我创建了这个简单的模块并将其命名为 MyModule.py:

import threading
import multiprocessing
import time

def workerThreaded():
    print 'thread working...'
    time.sleep(2)
    print 'thread complete'

def workerProcessed():
    print 'process working...'
    time.sleep(2)
    print 'process complete'

def main():
    workerThread = threading.Thread(target=workerThreaded)
    workerThread.start()
    workerProcess = multiprocessing.Process(target=workerProcessed)
    workerProcess.start()
    workerThread.join()
    workerProcess.join()

if __name__ == '__main__':
    main()

然后我把它放在一起进行单元测试:

import unittest
import MyModule

class MyModuleTester(unittest.TestCase):
    def testMyModule(self):
        MyModule.main()

unittest.main()

(我知道这不是一个好的单元测试,因为它实际上并没有测试它,它只是运行它,但这与我的问题无关)

如果我在 PyCharm 中使用代码覆盖率运行此单元测试,那么它只会显示 workerThreaded()main() 函数内的代码被覆盖,即使它显然也覆盖了 workerProcessed() 函数。

如何让 PyC​​harm 将在新进程进程中启动的代码包含在其代码覆盖范围中?另外,我怎样才能让它也包含if __name__ == '__main__': 块?

我正在运行 PyCharm 2.7.3 以及 Python 2.7.3。

【问题讨论】:

    标签: python unit-testing code-coverage pycharm


    【解决方案1】:

    Coverage.py 可以测量子进程中运行的代码,详情在http://nedbatchelder.com/code/coverage/subprocess.html

    【讨论】:

    • 我将 COVERAGE_PROCESS_START 环境变量设置为“/users/username/.coveragerc”,并在我的 PYTHONPATH 中列出的目录中创建了一个 sitecustomize.py,其中包含您页面中的两行代码,但 PyCharm 仍然没有将 workerProcessed 中的代码显示为被覆盖。
    【解决方案2】:

    我设法让它与子进程一起工作,不确定这是否适用于线程或 python 2。

    在您的项目根目录中创建 .covergerc 文件

    [run]
    concurrency=multiprocessing
    

    在您的项目根目录中创建 sitecustomize.py 文件

    import atexit
    from glob import glob
    
    import os
    from functools import partial
    from shutil import copyfile
    from tempfile import mktemp
    
    
    def combine_coverage(coverage_pattern, xml_pattern, old_coverage, old_xml):
        from coverage.cmdline import main
        # Find newly created coverage files
        coverage_files = [file for file in glob(coverage_pattern) if file not in old_coverage]
        xml_files = [file for file in glob(xml_pattern) if file not in old_xml]
    
        if not coverage_files:
            raise Exception("No coverage files generated!")
    
        if not xml_files:
            raise Exception("No coverage xml file generated!")
    
        # Combine all coverage files
        main(["combine", *coverage_files])
    
        # Convert them to xml
        main(["xml"])
    
        # Copy combined xml file over PyCharm generated one
        copyfile('coverage.xml', xml_files[0])
        os.remove('coverage.xml')
    
    
    def enable_coverage():
        import coverage
        # Enable subprocess monitoring by providing rc file and enable coverage collecting
        os.environ['COVERAGE_PROCESS_START'] = os.path.join(os.path.dirname(__file__), '.coveragerc')
        coverage.process_startup()
    
        # Get current coverage files so we can process only newly created ones
        temp_root = os.path.dirname(mktemp())
        coverage_pattern = '%s/pycharm-coverage*.coverage*' % temp_root
        xml_pattern = '%s/pycharm-coverage*.xml' % temp_root
        old_coverage = glob(coverage_pattern)
        old_xml = glob(xml_pattern)
    
        # Register atexit handler to collect coverage files when python is shutting down
        atexit.register(partial(combine_coverage, coverage_pattern, xml_pattern, old_coverage, old_xml))
    
    
    if os.getenv('PYCHARM_RUN_COVERAGE'):
        enable_coverage()
    

    这基本上检测代码是否在 PyCharm Coverage 中运行并收集新生成的覆盖率文件。有多个文件,一个用于主进程,一个用于每个子进程。所以我们需要将它们与“coverage combine”结合起来,然后使用“coverage xml”将它们转换为 xml,并将生成的文件复制到 PyCharm 生成的 xml 文件中。

    请注意,如果您在测试中杀死子进程,coverage.py 将不会写入数据文件。

    它不需要其他任何东西,只需点击 PyCharm 中的“Run unittests with Coverage”按钮即可。

    就是这样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-31
      • 2018-11-09
      • 1970-01-01
      • 1970-01-01
      • 2012-01-18
      • 1970-01-01
      • 2015-05-05
      • 2021-01-29
      相关资源
      最近更新 更多