PHPackages                             keboola/phpunit-retry-annotations - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. keboola/phpunit-retry-annotations

ActiveLibrary[Testing &amp; Quality](/categories/testing)

keboola/phpunit-retry-annotations
=================================

Traits for retrying test methods and classes in PHPUnit

v0.5(2y ago)047.8k↓30.2%4Apache-2.0PHPPHP ^7.4|^8.0

Since Dec 6Pushed 2y agoCompare

[ Source](https://github.com/keboola/phpunit-retry-annotations)[ Packagist](https://packagist.org/packages/keboola/phpunit-retry-annotations)[ Docs](https://github.com/keboola/phpunit-retry-annotations)[ RSS](/packages/keboola-phpunit-retry-annotations/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (3)Dependencies (5)Versions (9)Used By (4)

PHPUnit Retry
=============

[](#phpunit-retry)

Traits for retrying test methods and classes in PHPUnit.

Installation
------------

[](#installation)

```
composer require --dev keboola/phpunit-retry-annotations

```

Configuring retries
-------------------

[](#configuring-retries)

The default retries count is set to 3, when you want to change default retries for all tests copy&amp;paste file `phpunit-retry.xml.dist` from this library into the root directory of your application and change value of `baseRetryCount`

### Retry using a specified number of retries

[](#retry-using-a-specified-number-of-retries)

```
/**
 * @retryAttempts 2
 */
class MyTest extends PHPUnit\Framework\TestCase
{
    use PHPUnitRetry\RetryTrait;

    public function testSomethingFlakeyTwice()
    {
        // Retry a flakey test up to two times
    }

    /**
     * @retryAttempts 3
     */
    public function testSomethingFlakeyThreeTimes()
    {
        // Retry a flakey test up to three times
    }
}
```

**NOTE:** "Attempts" represents the number of times a test is retried. Providing "@retryAttempts" a value of 0 has no effect, and would not retry.

### Retry until a specific duration has passed

[](#retry-until-a-specific-duration-has-passed)

```
/**
 * @retryForSeconds 90
 */
class MyTest extends PHPUnit\Framework\TestCase
{
    use PHPUnitRetry\RetryTrait;

    public function testSomethingFlakeyFor90Seconds()
    {
        // retries for 90 seconds
    }

    /**
     * @retryForSeconds 1800
     */
    public function testSomethingFlakeyFor30Minutes()
    {
        // retries for 30 minutes
    }
}
```

Configuring retry conditions
----------------------------

[](#configuring-retry-conditions)

### Retry only for certain exceptions

[](#retry-only-for-certain-exceptions)

By default, retrying happens when any exception other than `PHPUnit\Framework\IncompleteTestError` and `PHPUnit\Framework\SkippedTestError`is thrown.

Because you may not always want to retry, you can configure your test to only retry under certain conditions. For example, you can only retry if your tests throw a certain exception.

```
/**
 * @retryAttempts 3
 * @retryIfException MyApi\ResourceExhaustedException
 */
```

You can retry for multiple exceptions.

```
/**
 * @retryAttempts 3
 * @retryIfException MyApi\RateLimitExceededException
 * @retryIfException ServiceUnavailableException
 */
```

### Retry based on a custom method

[](#retry-based-on-a-custom-method)

For more complex logic surrounding whether you should retry, define a custom retry method:

```
/**
 * @retryAttempts 3
 * @retryIfMethod isRateLimitExceededException
 */
public function testWithCustomRetryMethod()
{
    // retries only if the method `isRateLimitExceededException` returns true.
}

/**
 * @param Exception $e
 */
private function isRateLimitExceededException(Exception $e)
{
    // Check if HTTP Status code is 429 "Too many requests"
    return ($e instanceof HttpException && $e->getStatusCode() == 429);
}
```

Define arbitrary arguments for your retry method by passing them into the annotation:

```
/**
 * @retryAttempts 3
 * @retryIfMethod exceptionStatusCode 429
 */
public function testWithCustomRetryMethod()
{
    // retries only if the method `exceptionStatusCode` returns true.
}

/**
 * @param Exception $e
 */
private function exceptionStatusCode(Exception $e, $statusCode)
{
    // Check if HTTP status code is $statusCode
    return ($e instanceof HttpException && $e->getStatusCode() == $statusCode);
}
```

Configuring delay
-----------------

[](#configuring-delay)

### Delay for a duration between each retry

[](#delay-for-a-duration-between-each-retry)

```
/**
 * @retryAttempts 3
 * @retryDelaySeconds 10
 */
```

### Delay for an amount increasing exponentially based on the retry attempt

[](#delay-for-an-amount-increasing-exponentially-based-on-the-retry-attempt)

```
/**
 * @retryAttempts 3
 * @retryDelayMethod exponentialBackoff
 */
```

The behavior of the `exponentialBackoff` method is to start at 1 second and increase to a maximum of 60 seconds. The maximum delay can be customized by supplying a second argument to the annotation

```
/**
 * This test will delay with exponential backoff, with a maximum delay of 10 minutes.
 *
 * @retryAttempts 30
 * @retryDelayMethod exponentialBackoff 600
 */
```

### Define a custom delay method

[](#define-a-custom-delay-method)

```
/**
 * @retryAttempts 3
 * @retryDelayMethod myCustomDelay
 */
public function testWithCustomDelay()
{
    // retries using the method `myCustomDelay`.
}

/**
 * @param int $attempt The current test attempt
 */
private function myCustomDelay($attempt)
{
    // Doubles the sleep each attempt, but not longer than 10 seconds.
    sleep(min($attempt * 2, 10));
}
```

Define arbitrary arguments for your delay function by passing them into the annotation:

```
/**
 * @retryAttempts 3
 * @retryDelayMethod myCustomDelay 10 60
 */
public function testWithCustomDelay()
{
    // retries using the method `myCustomDelay`.
}

/**
 * @param int $attempt The current test attempt.
 * @param int $multiplier Rate of exponential backoff delay.
 * @param int $maxDelay Maximum time to wait regardless of retry attempt.
 */
private function myCustomDelay($attempt, $multiplier, $maxDelay)
{
    // Increases exponentially
    sleep(min($attempt * $multiplier, $max));
}
```

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity29

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity58

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 58.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~388 days

Total

5

Last Release

802d ago

PHP version history (3 changes)v0.1PHP ^7.1

v0.2PHP ^7.1|^8.0

v0.3PHP ^7.4|^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/101dbf2551a0709ddab522f97669f13a2c4cc2d0a1e8d009f3af6ba80accb1a9?d=identicon)[Keboola](/maintainers/Keboola)

---

Top Contributors

[![romanbracinik](https://avatars.githubusercontent.com/u/6448364?v=4)](https://github.com/romanbracinik "romanbracinik (27 commits)")[![bshaffer](https://avatars.githubusercontent.com/u/103941?v=4)](https://github.com/bshaffer "bshaffer (12 commits)")[![theofidry](https://avatars.githubusercontent.com/u/5175937?v=4)](https://github.com/theofidry "theofidry (4 commits)")[![tomasfejfar](https://avatars.githubusercontent.com/u/642928?v=4)](https://github.com/tomasfejfar "tomasfejfar (3 commits)")

---

Tags

phpunittestretry

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/keboola-phpunit-retry-annotations/health.svg)

```
[![Health](https://phpackages.com/badges/keboola-phpunit-retry-annotations/health.svg)](https://phpackages.com/packages/keboola-phpunit-retry-annotations)
```

###  Alternatives

[ta-tikoma/phpunit-architecture-test

Methods for testing application architecture

10745.9M13](/packages/ta-tikoma-phpunit-architecture-test)[kenjis/ci-phpunit-test

An easier way to use PHPUnit with CodeIgniter 3.x

5861.2M3](/packages/kenjis-ci-phpunit-test)[php-mock/php-mock-phpunit

Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.

1718.2M399](/packages/php-mock-php-mock-phpunit)[ergebnis/phpunit-slow-test-detector

Provides facilities for detecting slow tests in phpunit/phpunit.

1468.1M72](/packages/ergebnis-phpunit-slow-test-detector)[zenstruck/assert

Standalone, lightweight, framework agnostic, test assertion library.

8214.9M8](/packages/zenstruck-assert)[wp-phpunit/wp-phpunit

WordPress core PHPUnit library

803.7M208](/packages/wp-phpunit-wp-phpunit)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
