-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
#[Retry]
attribute to support retrying flaky test
#6182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
#[Retry]
attribute to support retrying flaky test
#6182
Conversation
This PR could also offer a complementary approach to the discussion in #5718: Bring back --repeat CLI option. While Thanks again, looking forward to your feedback! 😊 |
#[Retry]
attribute to support retrying flaky test
@@ -1276,6 +1278,12 @@ private function runTest(): mixed | |||
} | |||
|
|||
if (!$this->shouldExceptionExpectationsBeVerified($exception)) { | |||
$metadata = $this->getRetryMetadata($exception, $attempt); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only had a very cursory look at the proposed changes yet, but this caught my eye: how are tests retried that use expectException()
, for example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing that out!
Just to clarify how this works: in PHPUnit, if an exception is thrown after calling expectException()
, it's treated as a failure, not an error and failures are not retried. On the other hand, if an exception is thrown before expectException()
is set, PHPUnit treats it as an error, which will trigger a retry.
For example:
#[Retry(2)]
public function testFoo(): void
{
throw new Exception;
$this->expectException(Exception::class);
}
This will be retried, since the exception occurs before the expected exception is declared.
Whereas:
#[Retry(2)]
public function testFoo(): void
{
$this->expectException(Exception::class);
throw new Exception;
}
This will not be retried, because the exception is expected and treated as a failure if something goes wrong, not an unexpected error.
So i believe (maybe I'm wrong) that the the retry behavior aligns with how PHPUnit distinguishes between errors and failures.
Thanks again for your comment
I really appreciate the feedback 😄 .
src/Framework/TestCase.php
Outdated
return null; | ||
} | ||
|
||
$metadatas = MetadataRegistry::parser()->forMethod($this::class, $this->name())->isRetry()->getIterator(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The manual call to getIterator()
is superfluous as MetadataCollection
implements IteratorAggregate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the comment 😄
good catch. I agree with you, the manual call to getIterator()
is unnecessary given that MetadataCollection
implements IteratorAggregate
. I'll make that change.
2a68f67
to
3343e8e
Compare
3343e8e
to
1ff940d
Compare
✨ Add Retry Attribute for Retrying Flaky Tests
This PR introduces a new Retry attribute that can be applied to individual test methods in PHPUnit. Its goal is to help deal with flaky or unstable tests that occasionally fail due to non-deterministic reasons (e.g. network issues, race conditions, external dependencies).
🔧 How it works
The Retry attribute allows a test to be automatically retried a specified number of times before being marked as failed. You can also control the delay between retries, and optionally restrict retries to specific exception types.
🧪 Usage
Here’s how you can use it:
You can also specify a delay (in seconds) between retries:
And restrict retries to certain exception types only:
#[Retry(4, retryOn: TimeoutException::class)]
All parameters:
✅ Example
📈 Benefits
🔄 Prior art
Other popular testing frameworks already provide similar functionality: