【问题标题】:PyTest test-suite passing when asserts should fail when testing another thread's results in Python?在 Python 中测试另一个线程的结果时,当断言失败时,PyTest 测试套件是否通过?
【发布时间】:2018-11-28 19:23:25
【问题描述】:

我目前正在使用 pytest 来测试现有的(unittest test-suite per the documentation)。我目前正在编写一个等待分配 IP 地址然后将其返回给回调函数的线程,并且我正在编写单元测试来配合它。

这是我编写的测试用例类。

class TestGetIpAddressOnNewThread(unittest.TestCase):

    def test_get_existing_ip(self):
        def func(ip):
            assert ip == "192.168.0.1" # Not the real IP
            # Even when I introduce an assert statement that should fail, test still passes
            assert ip == "shouldn't be the ip" 

        ip_check = GetInstanceIpThread(instance, func)
        ip_check.start()
        ip_check.join()

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

这是GetInstanceIpThread 伪定义:

class GetInstanceIpThread(threading.Thread):
    def __init__(self, instance, callback):
        threading.Thread.__init__(self)
        self.event = threading.Event()
        self.instance = instance
        self.callback = callback

    def run(self):
        self.instance.wait_until_running()

        ip = self.instance.ip_address
        self.callback(ip)

当我使用pytest path_to_file.py::TestGetIpAddressOnNewThread 运行这个测试用例时,它通过了(耶!)但即使我引入了应该 100% 失败的断言语句(嘘!)。出了什么问题,我该如何编写实际上失败的测试?

【问题讨论】:

    标签: python multithreading testing pytest python-multithreading


    【解决方案1】:

    所以我正在回答我自己的问题,因为虽然我能够在 StackOverflow 上找到答案,但它不在任何有用的关键字下,因为大多数答案都在谈论如何使用 pytest-xdist 进行多线程测试,而不是测试多线程.我最终在调试过程中使用了pytest -s path_to_file.py::TestGetIpAddressOnNewThread,正如提到的here,这表明我有一个打印错误导致的异常,但并未导致测试失败。

    这让我找到了this question,我最初忽略了它,因为我没有意识到断言只是引发了 AssertError。

    因此,我对community wiki answer 进行了如下调整,以使断言正常工作!

    class GetInstanceIpThread(threading.Thread):
        def __init__(self, instance, callback=None):
            threading.Thread.__init__(self)
            self.event = threading.Event()
            self.instance = instance
            self.callback = callback
    
        def _run(self):
            # The original run code goes here
            self.instance.wait_until_running()
    
            ip = self.instance.ip_address
            self.callback(ip)
    
        def run(self):
            self.exc = None
            try:
                self._run()
            except BaseException as e:
                self.exc = e
    
        def join(self):
            super(GetInstanceIpThread, self).join()
            if self.exc:
                raise self.exc
    

    请注意,只要其他线程中有任何异常,您的测试就会失败。这可能不是您想要的,因此如果您只想在断言失败或类似情况下失败,您可以将 BaseException 更改为 AssertError(或任何您想要失败的内容)。

    重写 join() 是必要的,因为您必须在 pytest 的主线程上引发异常,以便它正确地使测试失败。

    【讨论】:

    • 如果有人有更好的解决方案,请随时分享。如果没有其他人提供更好的解决方案,我会在一两周内接受我的回答。
    • 我认为是 AssertionError,而不是 AssertError,对吧?
    【解决方案2】:

    我遇到了同样的问题,但无法访问创建线程的代码。我为该用例发布了一个小测试帮助程序包,pytest-reraise

    pip install pytest-reraise

    Pytest 风格的测试用例:

    def test_get_existing_ip(reraise):
        def func(ip):
            with reraise:
                assert ip == "192.168.0.1" # Not the real IP
                assert ip == "shouldn't be the ip" 
    
        ip_check = GetInstanceIpThread(instance, func)
        ip_check.start()
        ip_check.join()
    

    单元测试风格的测试用例:

    from pytest-reraise import Reraise
    
    class TestGetIpAddressOnNewThread(unittest.TestCase):
    
        def test_get_existing_ip(self):
            reraise = Reraise()
    
            def func(ip):
                with reraise:
                    assert ip == "192.168.0.1" # Not the real IP
                    assert ip == "shouldn't be the ip" 
    
            ip_check = GetInstanceIpThread(instance, func)
            ip_check.start()
            ip_check.join()
    
            # Re-raise the first exception that the `reraise` context manager captured:
            reraise()
    

    两个测试用例都按预期失败并报告AssertionError

    【讨论】:

      猜你喜欢
      • 2019-10-09
      • 2020-08-06
      • 2015-10-10
      • 1970-01-01
      • 2021-10-25
      • 1970-01-01
      • 2017-04-10
      • 2011-06-11
      相关资源
      最近更新 更多