Make AbstractTestNGSpringContextTests thread-safe regarding tracked exceptions

Prior to this commit, AbstractTestNGSpringContextTests was not
thread-safe with regard to tracked exceptions.

To address that, AbstractTestNGSpringContextTests now tracks the test
exception via a ThreadLocal.

Closes gh-35528
This commit is contained in:
Sam Brannen
2025-09-23 13:07:22 +02:00
parent e9fb5eb38a
commit 5cd2cb38e1
7 changed files with 159 additions and 28 deletions
@@ -74,8 +74,7 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
private final TestContextManager testContextManager;
@Nullable
private Throwable testException;
private final ThreadLocal<Throwable> testException = new ThreadLocal<>();
/**
@@ -141,31 +140,33 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
public void run(IHookCallBack callBack, ITestResult testResult) {
Method testMethod = testResult.getMethod().getConstructorOrMethod().getMethod();
boolean beforeCallbacksExecuted = false;
Throwable currentException = null;
try {
this.testContextManager.beforeTestExecution(this, testMethod);
beforeCallbacksExecuted = true;
}
catch (Throwable ex) {
this.testException = ex;
currentException = ex;
}
if (beforeCallbacksExecuted) {
callBack.runTestMethod(testResult);
this.testException = getTestResultException(testResult);
currentException = getTestResultException(testResult);
}
try {
this.testContextManager.afterTestExecution(this, testMethod, this.testException);
this.testContextManager.afterTestExecution(this, testMethod, currentException);
}
catch (Throwable ex) {
if (this.testException == null) {
this.testException = ex;
if (currentException == null) {
currentException = ex;
}
}
if (this.testException != null) {
throwAsUncheckedException(this.testException);
if (currentException != null) {
this.testException.set(currentException);
throwAsUncheckedException(currentException);
}
}
@@ -180,10 +181,10 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
@AfterMethod(alwaysRun = true)
protected void springTestContextAfterTestMethod(Method testMethod) throws Exception {
try {
this.testContextManager.afterTestMethod(this, testMethod, this.testException);
this.testContextManager.afterTestMethod(this, testMethod, this.testException.get());
}
finally {
this.testException = null;
this.testException.remove();
}
}