org.junitpioneer.jupiter.RetryingTest Maven / Gradle / Ivy
/*
* Copyright 2016-2023 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v20.html
*/
package org.junitpioneer.jupiter;
import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.parallel.Execution;
import org.junitpioneer.internal.TestNameFormatter;
/**
* {@code @RetryingTest} is a JUnit Jupiter extension that retries
* a failing test a certain number of times before the test actually
* shows up as failing.
*
* If annotated with {@code @RetryingTest(n)}, a test method is
* executed as long as it keeps failing, but no more than {@code n}
* times. That means all actual executions - except possibly the
* last - have failed. In contrast, all executions - except possibly
* the last - show up as being ignored/aborted because that is the best
* way to communicate a problem without breaking the test suite. Only
* if all {@code n} executions fail, is the last one marked as such.
* Each ignored/aborted or failed execution includes the underlying
* exception.
*
* By default the test will be retried on all exceptions except
* {@link org.opentest4j.TestAbortedException TestAbortedException}
* (which will abort the test entirely). To only retry on specific
* exceptions, use {@link RetryingTest#onExceptions() onExceptions()}.
*
* {@code @RetryingTest} has a number of limitations:
*
*
* - it can only be applied to methods
* - methods annotated with this annotation MUST NOT be annotated with {@code @Test}
* to avoid multiple executions!
* - it can't be used with other {@link TestTemplate}-based mechanisms
* like {@code org.junit.jupiter.api.RepeatedTest @RepeatedTest} or
* {@code org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}
* - it can't be used with {@code org.junit.jupiter.api.DynamicTest @DynamicTest}
* - all retries are run sequentially, even when used with
* parallel test execution
*
*
* During
* parallel test execution,
* all repetitions of a {@code RetryingTest} are executed sequentially to guarantee thread-safety.
*
* For more details and examples, see
* the documentation on @RetryingTest
.
*
* Before version 0.7.0 this annotation was called {@code @RepeatFailedTest}.
*
* @since 0.4
*/
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
// the extension is inherently thread-unsafe (has to wait for one execution before starting the next),
// so it forces execution of all retries onto the same thread
@Execution(SAME_THREAD)
@ExtendWith(RetryingTestExtension.class)
@TestTemplate
public @interface RetryingTest {
/**
* Placeholder for the display name of a {@code @RetryingTest}:
* {displayName}
*
* @since 1.7.0
*
* @see #name
*/
String DISPLAY_NAME_PLACEHOLDER = TestNameFormatter.DISPLAY_NAME_PLACEHOLDER;
/**
* Placeholder for the current invocation index of a {@code @RetryingTest}
* method (1-based): {index}
*
* @since 1.7.0
*
* @see #name
*/
String INDEX_PLACEHOLDER = TestNameFormatter.INDEX_PLACEHOLDER;
/**
* The display name to be used for individual invocations of the
* parameterized test; never blank or consisting solely of whitespace.
*
* Defaults to [{index}] {arguments}.
*
* Supported placeholders:
*
* - {@link org.junitpioneer.jupiter.RetryingTest#DISPLAY_NAME_PLACEHOLDER}
* - {@link org.junitpioneer.jupiter.RetryingTest#INDEX_PLACEHOLDER}
*
* You may use {@link java.text.MessageFormat} patterns
* to customize formatting.
*
* @since 1.7.0
*
* @see java.text.MessageFormat
* @see org.junit.jupiter.params.ParameterizedTest#name()
*/
String name() default "[{index}]";
/**
* Specifies how often the test is executed at most.
*
* Alias for {@link #maxAttempts()}.
*/
int value() default 0;
/**
* Specifies how often the test is executed at most.
*
* Either this or {@link #value()} are required.
* The value must be greater than {@link #minSuccess()}.
*/
int maxAttempts() default 0;
/**
* Specifies the minimum number of successful executions of the test.
*
* The test will be executed at least this number of times. If the test does not complete
* successfully the given number of times, the test will fail.
*
* Value must be greater than or equal to 1.
*/
int minSuccess() default 1;
/**
* Specifies a pause (in milliseconds) between executions.
*
* The thread executing this test is sleeping during that time
* and won't execute other tests, so long suspensions are discouraged.
*
* Value must be greater than or equal to 0.
*/
int suspendForMs() default 0;
/**
* Specifies on which exceptions a failed test is retried.
*
* If no exceptions are specified, tests will always be retried; otherwise only when it throws
* an exception that can be assigned to one of the specified types.
*/
// for the rationale to handle Throwable instead of just Exception, see
// explanation in org.junit.jupiter.api.function.Executable
Class extends Throwable>[] onExceptions() default {};
}