Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Retry Laravel Package

testo/retry

Testo Retry plugin: automatically rerun failed tests using a configurable retry policy. Helps reduce CI noise from flaky, transient failures and keep pipelines stable while issues are investigated. Installed as a dev dependency via composer require --dev testo/retry.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps for Laravel/Non-Testo Users

Since testo/retry is Testo-specific, Laravel developers using Pest/PHPUnit should first evaluate alternatives (e.g., Pest plugins or custom traits). However, if you’re already using Testo, follow these steps:

  1. Install Testo and the Retry Plugin:

    composer require --dev php-testo/testo testo/retry
    
  2. Enable the Retry Plugin in testo.php:

    return [
        'plugins' => [
            Testo\Retry\RetryPlugin::class,
        ],
        'retry' => [
            'enabled' => true,
            'policy' => 'exponential', // or 'fixed'
            'max_attempts' => 3,
            'delay' => 100, // milliseconds
        ],
    ];
    
  3. Run Tests with Retries:

    vendor/bin/testo
    
    • Failed tests will retry based on the configured policy.

First Use Case: CI Stability

For a Laravel/Pest project, if you still want to leverage this package:

  • Option 1: Use it only for Testo-based tests (e.g., legacy or framework-specific tests).
  • Option 2: Extract the retry logic (e.g., RetryPolicy class) and adapt it for Pest/PHPUnit:
    // Example: Create a Pest-compatible retry trait
    use Testo\Retry\RetryPolicy;
    
    trait PestRetryTrait {
        protected function retryTest(callable $test, int $maxAttempts = 3): void {
            $policy = new RetryPolicy('fixed', $maxAttempts, 100);
            $policy->execute($test);
        }
    }
    

Implementation Patterns

Workflow: Integrating with Testo

  1. Configure Retry Policies:

    • Fixed Delay: Retry with a constant delay (e.g., 100ms between attempts).
      'retry' => [
          'policy' => 'fixed',
          'delay' => 100,
          'max_attempts' => 3,
      ],
      
    • Exponential Backoff: Increase delay between retries (e.g., 100ms, 200ms, 400ms).
      'retry' => [
          'policy' => 'exponential',
          'delay' => 100,
          'max_attempts' => 3,
      ],
      
  2. Tag Tests for Retry (if supported):

    #[Test]
    #[Retry] // Hypothetical tag (check Testo docs)
    public function flaky_api_test(): void {
        // Test code that may fail transiently
    }
    
  3. Global vs. Per-Test Retries:

    • Global: Apply to all tests via config.
    • Per-Test: Override policies for specific tests (if plugin supports it).

Workflow: Adapting for Laravel/Pest

  1. Extract Core Logic: Copy the RetryPolicy class from testo/retry into your project and modify it to work with Pest:

    // app/Traits/RetryTrait.php
    use Testo\Retry\RetryPolicy;
    
    trait RetryTrait {
        protected function retry(callable $callback, int $maxAttempts = 3): mixed {
            $policy = new RetryPolicy('exponential', $maxAttempts, 100);
            return $policy->execute($callback);
        }
    }
    
  2. Use in Pest Tests:

    use App\Traits\RetryTrait;
    
    it('retries on failure', function () {
        $this->retry(function () {
            // Flaky test logic
            if (rand(0, 1)) {
                throw new RuntimeException('Transient failure');
            }
            return true;
        });
    });
    
  3. Customize Policies: Extend the RetryPolicy class to add new strategies (e.g., jitter, custom conditions):

    class CustomRetryPolicy extends RetryPolicy {
        public function shouldRetry(Throwable $exception): bool {
            return $exception instanceof NetworkTimeoutException;
        }
    }
    

Integration Tips

  • CI/CD: Use retries for integration/API tests where transient failures are common.
  • Logging: Log retry attempts to distinguish flakes from bugs:
    'retry' => [
        'logger' => fn(string $message) => error_log($message),
    ],
    
  • Performance: Avoid retries for fast unit tests (minimal benefit).
  • Test Isolation: Ensure retries don’t interfere with test state (e.g., database transactions).

Gotchas and Tips

Pitfalls

  1. False Positives:

    • Retrying tests that should fail (e.g., actual bugs) masks real issues.
    • Fix: Exclude deterministic failures from retry policies or log retry history.
  2. Infinite Retries:

    • Misconfigured policies (e.g., max_attempts: -1) can cause tests to loop forever.
    • Fix: Always set max_attempts and use a circuit breaker for known flakes.
  3. CI Timeouts:

    • Retries increase test duration, risking CI timeouts.
    • Fix: Monitor test runtime; adjust delay or max_attempts for critical paths.
  4. Environment Mismatches:

    • Retries may work in CI but fail locally (or vice versa) due to environment differences.
    • Fix: Use containerized testing (e.g., Docker) to standardize environments.
  5. Testo-Specific Quirks:

    • The plugin may not support all Testo features (e.g., parallel tests).
    • Fix: Check the Testo docs for limitations.

Debugging Tips

  • Enable Verbose Logging: Configure the retry plugin to log each attempt:
    'retry' => [
        'logger' => fn(string $message) => Logger::info($message),
    ],
    
  • Isolate Flaky Tests: Run a single flaky test with retries to debug:
    vendor/bin/testo --filter="flaky_test"
    
  • Check Retry Conditions: Override shouldRetry() in a custom policy to filter exceptions:
    class CustomPolicy extends RetryPolicy {
        public function shouldRetry(Throwable $e): bool {
            return $e instanceof SocketException && $this->attempt < 3;
        }
    }
    

Extension Points

  1. Custom Policies: Extend Testo\Retry\RetryPolicy to add new strategies:

    class JitterPolicy extends RetryPolicy {
        protected function getDelay(): int {
            return parent::getDelay() + rand(0, 50); // Add jitter
        }
    }
    
  2. Pre/Post-Retry Hooks: Add callbacks before/after retries:

    'retry' => [
        'hooks' => [
            'before' => fn() => Logger::info('Retrying test...'),
            'after' => fn(Throwable $e) => Logger::error('Test failed after retries', ['exception' => $e]),
        ],
    ],
    
  3. Testo Plugin Integration: If using Testo, explore its event system to trigger retries on specific test events:

    Testo::on(TestFailed::class, function ($event) {
        if ($event->test->hasTag('retryable')) {
            $event->retry();
        }
    });
    

Laravel-Specific Workarounds

  • Pest Plugin: Create a Pest plugin to wrap testo/retry logic:
    // pest.php
    plugins([
        new class implements Pest\Plugin {
            public function process(TestCase $testCase): void {
                $testCase->retry(fn() => $testCase->run());
            }
        },
    ]);
    
  • Service Provider: Register a global retry helper for non-test use cases (e.g., HTTP clients):
    // app/Providers/AppServiceProvider.php
    use Testo\Retry\RetryPolicy;
    
    public function boot(): void {
        if (!app()->runningInConsole()) {
            app()->singleton('retry', fn() => new RetryPolicy('exponential', 3, 100));
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor