diff --git a/CHANGES.txt b/CHANGES.txt
index bcd59d2ed..71cc122c5 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
Current (7.10.0)
+Fixed: GITHUB-3041: TestNG 7.x DataProvider works in opposite to TestNG 6.x when retrying tests. (Krishnan Mahadevan)
Fixed: GITHUB-3066: How to dynamically adjust the number of TestNG threads after IExecutorFactory is deprecated? (Krishnan Mahadevan)
New: GITHUB-2874: Allow users to define ordering for TestNG listeners (Krishnan Mahadevan)
Fixed: GITHUB-3033: Moved ant support under own repository https://github.com/testng-team/testng-ant (Julien Herr)
diff --git a/testng-core-api/src/main/java/org/testng/IDataProviderMethod.java b/testng-core-api/src/main/java/org/testng/IDataProviderMethod.java
index 4011bdd5c..fb972de80 100644
--- a/testng-core-api/src/main/java/org/testng/IDataProviderMethod.java
+++ b/testng-core-api/src/main/java/org/testng/IDataProviderMethod.java
@@ -38,4 +38,12 @@ default boolean propagateFailureAsTestFailure() {
default Class extends IRetryDataProvider> retryUsing() {
return IRetryDataProvider.DisableDataProviderRetries.class;
}
+
+ /**
+ * @return - true
if TestNG should use data returned by the original data provider
+ * invocation, when a test method fails and is configured to be retried.
+ */
+ default boolean cacheDataForTestRetries() {
+ return true;
+ }
}
diff --git a/testng-core-api/src/main/java/org/testng/annotations/DataProvider.java b/testng-core-api/src/main/java/org/testng/annotations/DataProvider.java
index 9d8124dd8..d3223c68d 100644
--- a/testng-core-api/src/main/java/org/testng/annotations/DataProvider.java
+++ b/testng-core-api/src/main/java/org/testng/annotations/DataProvider.java
@@ -57,6 +57,12 @@
*/
boolean propagateFailureAsTestFailure() default false;
+ /**
+ * @return - true
if TestNG should use data returned by the original data provider
+ * invocation, when a test method fails and is configured to be retried.
+ */
+ boolean cacheDataForTestRetries() default true;
+
/**
* @return - An Class which implements {@link IRetryDataProvider} and which can be used to retry a
* data provider.
diff --git a/testng-core-api/src/main/java/org/testng/annotations/IDataProviderAnnotation.java b/testng-core-api/src/main/java/org/testng/annotations/IDataProviderAnnotation.java
index a13a198bd..570549970 100644
--- a/testng-core-api/src/main/java/org/testng/annotations/IDataProviderAnnotation.java
+++ b/testng-core-api/src/main/java/org/testng/annotations/IDataProviderAnnotation.java
@@ -40,4 +40,16 @@ public interface IDataProviderAnnotation extends IAnnotation {
* data provider.
*/
Class extends IRetryDataProvider> retryUsing();
+
+ /**
+ * @param cache - when set to true
, TestNG does not invoke the data provider again
+ * when retrying failed tests using a retry analyzer.
+ */
+ void cacheDataForTestRetries(boolean cache);
+
+ /**
+ * @return - true
if TestNG should use data returned by the original data provider
+ * invocation, when a test method fails and is configured to be retried.
+ */
+ boolean isCacheDataForTestRetries();
}
diff --git a/testng-core/src/main/java/org/testng/internal/DataProviderMethod.java b/testng-core/src/main/java/org/testng/internal/DataProviderMethod.java
index bce2a1e42..2fca52ad4 100644
--- a/testng-core/src/main/java/org/testng/internal/DataProviderMethod.java
+++ b/testng-core/src/main/java/org/testng/internal/DataProviderMethod.java
@@ -53,4 +53,9 @@ public boolean propagateFailureAsTestFailure() {
public Class extends IRetryDataProvider> retryUsing() {
return annotation.retryUsing();
}
+
+ @Override
+ public boolean cacheDataForTestRetries() {
+ return annotation.isCacheDataForTestRetries();
+ }
}
diff --git a/testng-core/src/main/java/org/testng/internal/Parameters.java b/testng-core/src/main/java/org/testng/internal/Parameters.java
index b08af6223..14460e2e6 100644
--- a/testng-core/src/main/java/org/testng/internal/Parameters.java
+++ b/testng-core/src/main/java/org/testng/internal/Parameters.java
@@ -442,7 +442,7 @@ private static void checkParameterTypes(
throw new TestNGException(
errPrefix
+ ".\nFor more information on native dependency injection please refer to "
- + "https://testng.org/doc/documentation-main.html#native-dependency-injection");
+ + "https://testng.org/#_dependency_injection");
}
}
diff --git a/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java b/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java
index e793e72f4..680bcb6fb 100644
--- a/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java
+++ b/testng-core/src/main/java/org/testng/internal/annotations/DataProviderAnnotation.java
@@ -13,6 +13,8 @@ public class DataProviderAnnotation extends BaseAnnotation implements IDataProvi
private boolean m_bubbleUpFailures = false;
private Class extends IRetryDataProvider> retryUsing;
+ private boolean cachedDataForTestRetries = true;
+
@Override
public boolean isParallel() {
return m_parallel;
@@ -62,4 +64,13 @@ public void setRetryUsing(Class extends IRetryDataProvider> retry) {
public Class extends IRetryDataProvider> retryUsing() {
return retryUsing;
}
+
+ public void cacheDataForTestRetries(boolean cache) {
+ this.cachedDataForTestRetries = cache;
+ }
+
+ @Override
+ public boolean isCacheDataForTestRetries() {
+ return cachedDataForTestRetries;
+ }
}
diff --git a/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java b/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java
index 000180101..ffb6e74ed 100644
--- a/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java
+++ b/testng-core/src/main/java/org/testng/internal/annotations/JDK15TagFactory.java
@@ -483,6 +483,7 @@ private IAnnotation createDataProviderTag(Method method, Annotation a) {
result.propagateFailureAsTestFailure();
}
result.setRetryUsing(c.retryUsing());
+ result.cacheDataForTestRetries(c.cacheDataForTestRetries());
return result;
}
diff --git a/testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java b/testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java
index 1d4adafe3..bcf0a4ab9 100644
--- a/testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java
+++ b/testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java
@@ -23,6 +23,7 @@
import org.testng.DataProviderHolder;
import org.testng.IClassListener;
import org.testng.IDataProviderListener;
+import org.testng.IDataProviderMethod;
import org.testng.IHookable;
import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
@@ -238,6 +239,24 @@ public FailureContext retryFailed(
failure.representsRetriedMethod.set(true);
do {
failure.instances = Lists.newArrayList();
+ boolean cacheData =
+ Optional.ofNullable(arguments.getTestMethod().getDataProviderMethod())
+ .map(IDataProviderMethod::cacheDataForTestRetries)
+ .orElse(false);
+ if (!cacheData) {
+ Map allParameters = Maps.newHashMap();
+ int verbose = testContext.getCurrentXmlTest().getVerbose();
+ ParameterHandler handler =
+ new ParameterHandler(
+ m_configuration.getObjectFactory(), annotationFinder(), this.holder, verbose);
+
+ ParameterBag bag =
+ handler.createParameters(
+ arguments.getTestMethod(), arguments.getParameters(), allParameters, testContext);
+ if (bag.hasErrors()) {
+ continue;
+ }
+ }
Object[] parameterValues = arguments.getParameterValues();
TestMethodArguments tma =
new TestMethodArguments.Builder()
diff --git a/testng-core/src/test/java/test/dataprovider/DataProviderTest.java b/testng-core/src/test/java/test/dataprovider/DataProviderTest.java
index 63a2c626e..80cebc0c8 100644
--- a/testng-core/src/test/java/test/dataprovider/DataProviderTest.java
+++ b/testng-core/src/test/java/test/dataprovider/DataProviderTest.java
@@ -55,12 +55,21 @@
import test.dataprovider.issue2934.TestCaseSample.CoreListener;
import test.dataprovider.issue2934.TestCaseSample.ToggleDataProvider;
import test.dataprovider.issue2980.LoggingListener;
+import test.dataprovider.issue3041.SampleTestCase;
import test.dataprovider.issue3045.DataProviderListener;
import test.dataprovider.issue3045.DataProviderTestClassSample;
import test.dataprovider.issue3045.DataProviderWithoutListenerTestClassSample;
public class DataProviderTest extends SimpleBaseTest {
+ @Test(description = "GITHUB-3041")
+ public void ensureDataProvidersCanBeInstructedNotToCacheDataForFailedTestRetries() {
+ TestNG testng = create(SampleTestCase.class);
+ testng.setVerbose(2);
+ testng.run();
+ assertThat(SampleTestCase.invocationCount.get()).isEqualTo(2);
+ }
+
@Test(description = "GITHUB-2819")
public void testDataProviderCanBeRetriedOnFailures() {
TestNG testng = create(TestClassUsingDataProviderRetrySample.class);
diff --git a/testng-core/src/test/java/test/dataprovider/issue3041/SampleTestCase.java b/testng-core/src/test/java/test/dataprovider/issue3041/SampleTestCase.java
new file mode 100644
index 000000000..e42e64d76
--- /dev/null
+++ b/testng-core/src/test/java/test/dataprovider/issue3041/SampleTestCase.java
@@ -0,0 +1,41 @@
+package test.dataprovider.issue3041;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.testng.IRetryAnalyzer;
+import org.testng.ITestResult;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class SampleTestCase {
+
+ public static final AtomicInteger invocationCount = new AtomicInteger(0);
+ private static final Random random = new Random();
+
+ @Test(dataProvider = "dp", retryAnalyzer = MyRetry.class)
+ public void testMethod(int i) {
+ if (invocationCount.get() != 2) {
+ throw new RuntimeException("Failed for " + i);
+ }
+ }
+
+ @DataProvider(name = "dp", cacheDataForTestRetries = false)
+ public Object[][] getData() {
+ invocationCount.incrementAndGet();
+ return new Object[][] {{next()}, {next()}};
+ }
+
+ private static int next() {
+ return random.nextInt();
+ }
+
+ public static class MyRetry implements IRetryAnalyzer {
+
+ private final AtomicInteger counter = new AtomicInteger(1);
+
+ @Override
+ public boolean retry(ITestResult result) {
+ return counter.getAndIncrement() != 2;
+ }
+ }
+}