Skillversitas

Retry Mechanism

Flaky steps are a fact of life when tests touch networks, browsers, or background jobs. Behave has no built-in retry, but retrying a step is a small piece of code: a decorator that re-runs the step function a few times with a delay and only raises once every attempt failed.

A Retry Decorator

# features/steps/retry.py
import functools
import time

def retry(times=3, delay=1):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(context, *args, **kwargs):
            last_exc = None
            for attempt in range(times):
                try:
                    return func(context, *args, **kwargs)
                except Exception as exc:  # noqa: BLE001
                    last_exc = exc
                    if attempt < times - 1:
                        time.sleep(delay)
            raise last_exc
        return wrapper
    return decorator

Applying Retry to a Step

from behave import step
from steps.retry import retry

@retry(times=3, delay=2)
@step("the payment gateway confirms the charge")
def step_impl(context):
    receipt = gateway.poll(context.charge_id, timeout=5)
    assert receipt is not None, f"no receipt for {context.charge_id}"

Order matters: the retry decorator must wrap the behave step decorator, so behave sees one stable function while the wrapper handles the re-execution. If an assertion still fails after the last attempt, the scenario fails with the original exception and traceback.

Rerunning Only Failed Scenarios

For whole-scenario reruns instead of per-step retries, the third-party behave-rerun plugin adds a formatter that records failures in a rerun file; the next run executes only those scenarios:

pip install behave-rerun

# first run records failures
behave -f rerun -o rerun.txt features/

# second run executes only the recorded failures
behave -r rerun.txt

Know When to Retry

Retry network hiccups and async propagation, never logic errors. If a retry hides a real bug, the test will pass locally and fail in a stranger's environment. Keep the retry count small, log each attempt, and treat "retried and passed" as a signal to investigate the flake.

Summary

Wrap flaky steps in a retry decorator that repeats with a delay, or use the behave-rerun plugin to rerun failed scenarios. Reserve retries for genuine instability and keep them visible in the output.