From eb8144da96e472f8c19a99b5a5f5af3281981e21 Mon Sep 17 00:00:00 2001 From: Krishnan Mahadevan Date: Tue, 1 Oct 2019 09:40:27 +0530 Subject: [PATCH] Handle exceptions from data provider Closes #2157 --- CHANGES.txt | 1 + .../DataProviderInvocationException.java | 11 ++++ .../java/org/testng/internal/TestInvoker.java | 9 ++++ .../dataprovider/FailingDataProviderTest.java | 12 +++++ ...sWithDataProviderThatThrowsExceptions.java | 50 +++++++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 src/main/java/org/testng/DataProviderInvocationException.java create mode 100644 src/test/java/test/dataprovider/issue2157/TestClassWithDataProviderThatThrowsExceptions.java diff --git a/CHANGES.txt b/CHANGES.txt index 3ceda265e2..8ceb0c28ef 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ Current +Fixed: GITHUB-2157: NullPointerException occurs when a Retried test has an exception in DataProvider (Krishnan Mahadevan) Fixed: GITHUB-2150: Upgraded jQuery from 1.7.1 to 3.4.1 to resolve reported prototype pollution vulnerability Fixed: GITHUB-2149: Handle NoClassDefFoundError when classloader fails to load a class New: GITHUB-2111: Provide an interceptor for Data Provider (Krishnan Mahadevan) diff --git a/src/main/java/org/testng/DataProviderInvocationException.java b/src/main/java/org/testng/DataProviderInvocationException.java new file mode 100644 index 0000000000..cbcdaff51c --- /dev/null +++ b/src/main/java/org/testng/DataProviderInvocationException.java @@ -0,0 +1,11 @@ +package org.testng; + +/** + * Represents any issues that arise out of invoking a data provider method. + */ +public class DataProviderInvocationException extends TestNGException { + + public DataProviderInvocationException(String string, Throwable t) { + super(string, t); + } +} diff --git a/src/main/java/org/testng/internal/TestInvoker.java b/src/main/java/org/testng/internal/TestInvoker.java index 3e0e884dbd..932b272a2c 100644 --- a/src/main/java/org/testng/internal/TestInvoker.java +++ b/src/main/java/org/testng/internal/TestInvoker.java @@ -15,6 +15,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.testng.DataProviderHolder; +import org.testng.DataProviderInvocationException; import org.testng.IClassListener; import org.testng.IDataProviderListener; import org.testng.IHookable; @@ -188,6 +189,14 @@ public FailureContext retryFailed( ParameterBag bag = handler.createParameters(arguments.getTestMethod(), arguments.getParameters(), allParameters, testContext); + ITestResult errorResult = bag.errorResult; + + if (errorResult != null ) { + Throwable cause = errorResult.getThrowable(); + String m = errorResult.getMethod().getMethodName(); + String msg = String.format("Encountered problems when gathering parameter values for [%s]. Root cause: ", m); + throw new DataProviderInvocationException(msg, cause); + } Object[] parameterValues = Parameters.getParametersFromIndex(Objects.requireNonNull(bag.parameterHolder).parameters, arguments.getParametersIndex()); diff --git a/src/test/java/test/dataprovider/FailingDataProviderTest.java b/src/test/java/test/dataprovider/FailingDataProviderTest.java index 510f4b792e..76339f54cd 100644 --- a/src/test/java/test/dataprovider/FailingDataProviderTest.java +++ b/src/test/java/test/dataprovider/FailingDataProviderTest.java @@ -1,8 +1,11 @@ package test.dataprovider; +import org.testng.DataProviderInvocationException; +import org.testng.ITestResult; import org.testng.annotations.Test; import test.InvokedMethodNameListener; import test.SimpleBaseTest; +import test.dataprovider.issue2157.TestClassWithDataProviderThatThrowsExceptions; import static org.assertj.core.api.Assertions.assertThat; @@ -33,4 +36,13 @@ public void failingDataProviderAndInvocationCount() { "testShouldSkipEvenIfSuccessPercentage", "testShouldSkipEvenIfSuccessPercentage"); } + + @Test(description = "GITHUB-2157") + public void abortWhenDataProviderThrowsException() { + InvokedMethodNameListener listener = run(TestClassWithDataProviderThatThrowsExceptions.class); + ITestResult result = listener.getResult("testMethod"); + Throwable cause = result.getThrowable(); + assertThat(cause).isInstanceOf(DataProviderInvocationException.class); + assertThat(result.getStatus()).isEqualTo(ITestResult.FAILURE); + } } diff --git a/src/test/java/test/dataprovider/issue2157/TestClassWithDataProviderThatThrowsExceptions.java b/src/test/java/test/dataprovider/issue2157/TestClassWithDataProviderThatThrowsExceptions.java new file mode 100644 index 0000000000..9d6e50e1c1 --- /dev/null +++ b/src/test/java/test/dataprovider/issue2157/TestClassWithDataProviderThatThrowsExceptions.java @@ -0,0 +1,50 @@ +package test.dataprovider.issue2157; + +import java.util.concurrent.atomic.AtomicInteger; +import org.testng.Assert; +import org.testng.IRetryAnalyzer; +import org.testng.ITestResult; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class TestClassWithDataProviderThatThrowsExceptions { + + @Test(dataProvider = "dp", retryAnalyzer = SimplyRetry.class) + public void testMethod(String i) { + if ("First".equalsIgnoreCase(i) || "Second".equalsIgnoreCase(i)) { + Assert.fail(); + } + } + + private static AtomicInteger counter = new AtomicInteger(); + + @DataProvider(name = "dp") + public static Object[][] dpWithException() { + return new Object[][]{ + {foo()}, + }; + } + + private static String foo(){ + counter.getAndIncrement(); + + if(counter.get() == 1){ + return "First"; + } + if(counter.get() == 2){ + return "Second"; + } + throw new RuntimeException("TestNG doesn't handle an exception"); + } + + public static class SimplyRetry implements IRetryAnalyzer { + + private static int attempts = 1; + + @Override + public boolean retry(ITestResult result) { + return attempts++ != 3; + } + } + +}