user10551458
user10551458

Reputation: 183

Can I retry for failed tests in Pytest

Can I ask for failed tests in pytest Some number of restarts (retries). For example: if 2 attempts fail and the third succeeds, then the total result succeeds

Upvotes: 15

Views: 17978

Answers (2)

Slam
Slam

Reputation: 8572

AFAIK, this is implemented in pytest-rerunfailures plugin. Take a look into.

e.g.

$ pip install pytest-rerunfailures
$ pytest --reruns 5

2023 update: later versions of pytest bundle relevant functionality

Upvotes: 26

Dorcioman
Dorcioman

Reputation: 636

Using the pytest-rerunfailures package is a good approach, but sometimes it may not be strong enough. If you need custom options, this implementation can be useful.

from pytest import fixture
from functools import wraps
import time


def retry_test(stop_max_attempt_number=5, wait_fixed=5):
    def decorator(test_func_ref):
        @wraps(test_func_ref)
        def wrapper(*args, **kwargs):
            retry_count = 1

            while retry_count < stop_max_attempt_number:
                try:
                    return test_func_ref(*args, **kwargs)

                except AssertionError as assert_error:
                    assert_message, _ = assert_error.__str__().split("\n")
                    print(f"Retry error: \"{test_func_ref.__name__}\" --> {assert_message}. "
                          f"[{retry_count}/{stop_max_attempt_number - 1}] Retrying new execution in {wait_fixed} second(s)")
                    time.sleep(wait_fixed)
                    retry_count += 1

            # Preserve original traceback in case assertion Failed        
            return test_func_ref(*args, **kwargs)

        return wrapper
    return decorator


class Test:
    @fixture(scope="class")
    def env(self):
        return {
            "retry": 10,
            "sleep": 5,
            "data": iter([{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 10}])

        }

    @retry_test(stop_max_attempt_number=5, wait_fixed=2)
    def test_product(self, env):
        message_product = next(env["data"], None)
        print(f"Reading message. {message_product}")

        if message_product["id"] == 10:
            assert True, "Message was intercepted"

        else:
            assert False, f"No message arrived"

Output

Reading message. {'id': 1}
Retry error: "test_product" --> No message arrived. [1/4] Retrying new execution in 2 second(s)
Reading message. {'id': 2}
Retry error: "test_product" --> No message arrived. [2/4] Retrying new execution in 2 second(s)
Reading message. {'id': 3}
Retry error: "test_product" --> No message arrived. [3/4] Retrying new execution in 2 second(s)
Reading message. {'id': 4}
Retry error: "test_product" --> No message arrived. [4/4] Retrying new execution in 2 second(s)
Reading message. {'id': 5}
FAILED
retry_helper.py:40 (Test.test_product)
self = <helpers.retry_helper.Test object at 0x0000021F2793EF28>
env = {'data': <list_iterator object at 0x0000021F2793EF60>, 'retry': 10, 'sleep': 5}

    @retry_test(stop_max_attempt_number=5, wait_fixed=2)
    def test_product(self, env):
        message_product = next(env["data"], None)
        print(f"Reading message. {message_product}")
    
        if message_product["id"] == 10:
            assert True, "Message was intercepted"
    
        else:
>           assert False, f"No message arrived"
E           AssertionError: No message arrived
E           assert False

retry_helper.py:50: AssertionError

Upvotes: 5

Related Questions