Python单元测试和多线程

时间:2016-11-06 08:17:54

标签: python unit-testing python-3.x python-unittest

我正在使用python的unittest,并希望编写一个测试,启动一些线程并等待它们完成。线程执行一个具有一些unittest断言的函数。如果任何断言失败,我希望测试失败。情况似乎并非如此。

编辑:最小可运行示例(python3)

import unittest
import threading

class MyTests(unittest.TestCase):

 def test_sample(self):
  t = threading.Thread(target=lambda: self.fail())
  t.start()
  t.join()

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

,输出为:

sh-4.3$ python main.py -v                                                                                                                                                                                                              
test_sample (__main__.MyTests) ... Exception in thread Thread-1:                                                                                                                                                                       
Traceback (most recent call last):                                                                                                                                                                                                     
  File "/usr/lib64/python2.7/threading.py", line 813, in __bootstrap_inner                                                                                                                                                             
    self.run()                                                                                                                                                                                                                         
  File "/usr/lib64/python2.7/threading.py", line 766, in run                                                                                                                                                                           
    self.__target(*self.__args, **self.__kwargs)                                                                                                                                                                                       
  File "main.py", line 7, in <lambda>                                                                                                                                                                                                  
    t = threading.Thread(target=lambda: self.fail())                                                                                                                                                                                   
  File "/usr/lib64/python2.7/unittest/case.py", line 450, in fail                                                                                                                                                                      
    raise self.failureException(msg)                                                                                                                                                                                                   
AssertionError: None                                                                                                                                                                                                                   

ok                                                                                                                                                                                                                                     

----------------------------------------------------------------------                                                                                                                                                                 
Ran 1 test in 0.002s                                                                                                                                                                                                                   

OK     

4 个答案:

答案 0 :(得分:2)

Python * KeyStore: LOCKED * KeyGuard: OFF/ON * Action: old unlock dialog * Notes: assume old password, need to use it to unlock. * if unlock, ensure key guard before install. * if reset, treat as UNINITALIZED/OFF 断言由异常传达,因此您必须确保异常最终在主线程中。所以对于一个线程来说,这意味着你必须运行unittest,因为这会将线程中的异常抛出到主线程中:

.join()

另外,请确保在 t = threading.Thread(target=lambda: self.assertTrue(False)) t.start() t.join() 注册之前,您没有任何try/except块可能会占用此异常。

编辑unittest在从线程调用时确实无法传达,即使存在self.fail()也是如此。不知道该怎么做。

答案 1 :(得分:1)

您的测试不会失败,原因与该代码将显示“无异常”

import threading

def raise_err():
    raise Exception()

try:
    t = threading.Thread(target=raise_err)
    t.start()
    t.join()
    print('no exception')
except:
    print('caught exception')

unittest运行测试功能时,它将通过查看代码执行是否导致某些异常来确定通过/失败。如果异常发生在线程内部,则主线程中仍然没有异常。

如果您认为必须通过在线程中运行某些程序来获得通过/失败结果,则可以执行以下操作。但是,这实际上并不是单元测试旨在工作的方式,并且可能有一种更轻松的方法来完成您要完成的任务。

import threading
import unittest

def raise_err():
    raise Exception()
def no_err():
    return

class Runner():

    def __init__(self):
        self.threads = {}
        self.thread_results = {}

    def add(self, target, name):
        self.threads[name] = threading.Thread(target = self.run, args = [target, name])
        self.threads[name].start()

    def run(self, target, name):
        self.thread_results[name] = 'fail'
        target()
        self.thread_results[name] = 'pass'

    def check_result(self, name):
        self.threads[name].join()
        assert(self.thread_results[name] == 'pass')

runner = Runner()

class MyTests(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        runner.add(raise_err, 'test_raise_err')
        runner.add(no_err, 'test_no_err')

    def test_raise_err(self):
        runner.check_result('test_raise_err')

    def test_no_err(self):
        runner.check_result('test_no_err')

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

答案 2 :(得分:1)

使用current.futures.ThreadPoolExecutor或https://docs.python.org/3/library/threading.html#threading.excepthook收集线程中引发的异常

import unittest
import threading
from concurrent import futures

class catch_threading_exception:
    """
    https://docs.python.org/3/library/test.html#test.support.catch_threading_exception
    Context manager catching threading.Thread exception using
    threading.excepthook.

    Attributes set when an exception is catched:

    * exc_type
    * exc_value
    * exc_traceback
    * thread

    See threading.excepthook() documentation for these attributes.

    These attributes are deleted at the context manager exit.

    Usage:

        with support.catch_threading_exception() as cm:
            # code spawning a thread which raises an exception
            ...

            # check the thread exception, use cm attributes:
            # exc_type, exc_value, exc_traceback, thread
            ...

        # exc_type, exc_value, exc_traceback, thread attributes of cm no longer
        # exists at this point
        # (to avoid reference cycles)
    """

    def __init__(self):
        self.exc_type = None
        self.exc_value = None
        self.exc_traceback = None
        self.thread = None
        self._old_hook = None

    def _hook(self, args):
        self.exc_type = args.exc_type
        self.exc_value = args.exc_value
        self.exc_traceback = args.exc_traceback
        self.thread = args.thread

    def __enter__(self):
        self._old_hook = threading.excepthook
        threading.excepthook = self._hook
        return self

    def __exit__(self, *exc_info):
        threading.excepthook = self._old_hook
        del self.exc_type
        del self.exc_value
        del self.exc_traceback
        del self.thread


class MyTests(unittest.TestCase):
    def test_tpe(self):
        with futures.ThreadPoolExecutor() as pool:
            pool.submit(self.fail).result()

    def test_t_excepthook(self):
        with catch_threading_exception() as cm:
            t = threading.Thread(target=self.fail)
            t.start()
            t.join()
            if cm.exc_value is not None:
                raise cm.exc_value


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

答案 3 :(得分:0)

在我的主线程中,我通过检查退出代码来检测子进程失败(非零失败)。

proc.join()
self.assertEqual(proc.exitcode, 0, 'Sub-process failed, check output for stack trace')