PyTest测试套件在断言时通过时应该在Python中测试另一个线程的结果时失败?

问题描述 投票:3回答:2

我目前正在使用pytest来测试现有的(unittest测试套件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%失败的断言语句(boo!)。怎么了,我该如何编写实际上失败的测试?

python multithreading testing pytest python-multithreading
2个回答
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的主线程上引发异常,以使其正常通过测试。


0
投票

我遇到了同样的问题,但是无法访问创建线程的代码。我针对该用例发布了一个小测试帮助程序包,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()

Unittest风格的测试用例:

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

© www.soinside.com 2019 - 2024. All rights reserved.