Skip to content

Commit

Permalink
Add RuntimeBehavior property to allow OnAfterClass listeners to run a…
Browse files Browse the repository at this point in the history
…fter @afterclass configuration methods.

Closes #2796
  • Loading branch information
oliver-hughes authored and krmahadevan committed Jan 8, 2023
1 parent f6975f2 commit 8634720
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Current
Fixed: GITHUB-2796: Option for onAfterClass to run after @AfterClass
Fixed: GITHUB-2857: XmlTest index is not set for test suites invoked with YAML

7.7.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public final class RuntimeBehavior {
public static final String STRICTLY_HONOUR_PARALLEL_MODE = "testng.strict.parallel";
public static final String TESTNG_DEFAULT_VERBOSE = "testng.default.verbose";
public static final String IGNORE_CALLBACK_INVOCATION_SKIPS = "testng.ignore.callback.skip";
public static final String SYMMETRIC_LISTENER_EXECUTION = "testng.listener.execution.symmetric";

private RuntimeBehavior() {}

Expand Down Expand Up @@ -132,4 +133,17 @@ public static boolean enforceThreadAffinity() {
public static int getDefaultVerboseLevel() {
return Integer.getInteger(TESTNG_DEFAULT_VERBOSE, 1);
}

/**
* @return - <code>true</code> if we would like to invoke AfterClass methods symmetrically to
* BeforeClass. When true, order is:
* <ol>
* <li>Class @afterClass methods
* <li>Listener onAfterClass methods
* </ol>
* When false, order is reversed.
*/
public static boolean useSymmetricListenerExecution() {
return Boolean.parseBoolean(System.getProperty(SYMMETRIC_LISTENER_EXECUTION, "false"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,16 @@ protected void invokeAfterClassMethods(ITestClass testClass, IMethodInstance mi)
invokeInstances.add(inst);
}

for (IClassListener listener : m_listeners) {
listener.onAfterClass(testClass);
if (RuntimeBehavior.useSymmetricListenerExecution()) {
invokeAfterClassConfigurations(testClass, invokeInstances);
invokeListenersOnAfterClass(testClass, m_listeners);
} else {
invokeListenersOnAfterClass(testClass, m_listeners);
invokeAfterClassConfigurations(testClass, invokeInstances);
}
}

private void invokeAfterClassConfigurations(ITestClass testClass, List<Object> invokeInstances) {
for (Object invokeInstance : invokeInstances) {
ConfigMethodArguments attributes =
new Builder()
Expand All @@ -223,6 +230,12 @@ protected void invokeAfterClassMethods(ITestClass testClass, IMethodInstance mi)
}
}

private void invokeListenersOnAfterClass(ITestClass testClass, List<IClassListener> listeners) {
for (IClassListener listener : listeners) {
listener.onAfterClass(testClass);
}
}

protected int indexOf(ITestNGMethod tm, ITestNGMethod[] allTestMethods) {
for (int i = 0; i < allTestMethods.length; i++) {
if (allTestMethods[i] == tm) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.assertj.core.api.Assertions;
import org.testng.TestNG;
import org.testng.annotations.Test;
import org.testng.internal.RuntimeBehavior;
import test.SimpleBaseTest;
import test.listeners.issue1952.TestclassSample;

Expand Down Expand Up @@ -462,6 +463,102 @@ public void testOrderHasOnlyFailedMethodWithRetryMechanism() {
runTest(expected, SimpleTestClassWithFailedMethodHasRetryAnalyzer.class, true);
}

@Test(description = "Test Configuration/Listener order NOT using symmetric listener execution")
public void testOrderForNonSymmetricOnAfterClass() {
List<String> nonSymmetricExpected =
Arrays.asList(
IEXECUTIONLISTENER_ON_EXECUTION_START,
IALTERSUITELISTENER_ALTER,
IANNOTATIONTRANSFORMER_TRANSFORM_3_ARGS,
IANNOTATIONTRANSFORMER_TRANSFORM_4_ARGS,
ISUITELISTENER_ON_START,
ITESTLISTENER_ON_START_TEST_TAG,
METHODINTERCEPTOR_INTERCEPT,
METHODINTERCEPTOR_INTERCEPT,
// onBeforeClass
ICLASSLISTENER_ON_BEFORE_CLASS,
// @beforeClass
ICONFIGURATIONLISTENER_BEFORE_CONFIGURATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION_WITH_CONTEXT,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION_WITH_CONTEXT,
ICONFIGURATIONLISTENER_ON_CONFIGURATION_SUCCESS,
// test method
ITESTLISTENER_ON_START_TEST_METHOD,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION_WITH_CONTEXT,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION_WITH_CONTEXT,
ITESTLISTENER_ON_TEST_SUCCESS_TEST_METHOD,
// onAfterClass
ICLASSLISTENER_ON_AFTER_CLASS,
// @afterClass
ICONFIGURATIONLISTENER_BEFORE_CONFIGURATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION_WITH_CONTEXT,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION_WITH_CONTEXT,
ICONFIGURATIONLISTENER_ON_CONFIGURATION_SUCCESS,
IEXECUTION_VISUALISER_CONSUME_DOT_DEFINITION,
ITESTLISTENER_ON_FINISH_TEST_TAG,
ISUITELISTENER_ON_FINISH,
IREPORTER_GENERATE_REPORT,
IEXECUTIONLISTENER_ON_EXECUTION_FINISH);
runTest(nonSymmetricExpected, SimpleTestClassWithBeforeAndAfterClass.class);
}

@Test(description = "Test Configuration/Listener order using symmetric listener execution")
public void testOrderForSymmetricOnAfterClass() {
List<String> symmetricExpected =
Arrays.asList(
IEXECUTIONLISTENER_ON_EXECUTION_START,
IALTERSUITELISTENER_ALTER,
IANNOTATIONTRANSFORMER_TRANSFORM_3_ARGS,
IANNOTATIONTRANSFORMER_TRANSFORM_4_ARGS,
ISUITELISTENER_ON_START,
ITESTLISTENER_ON_START_TEST_TAG,
METHODINTERCEPTOR_INTERCEPT,
METHODINTERCEPTOR_INTERCEPT,
// onBeforeClass
ICLASSLISTENER_ON_BEFORE_CLASS,
// @beforeClass
ICONFIGURATIONLISTENER_BEFORE_CONFIGURATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION_WITH_CONTEXT,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION_WITH_CONTEXT,
ICONFIGURATIONLISTENER_ON_CONFIGURATION_SUCCESS,
// test method
ITESTLISTENER_ON_START_TEST_METHOD,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION_WITH_CONTEXT,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION_WITH_CONTEXT,
ITESTLISTENER_ON_TEST_SUCCESS_TEST_METHOD,
// @afterClass
ICONFIGURATIONLISTENER_BEFORE_CONFIGURATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION,
IINVOKEDMETHODLISTENER_BEFORE_INVOCATION_WITH_CONTEXT,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION,
IINVOKEDMETHODLISTENER_AFTER_INVOCATION_WITH_CONTEXT,
ICONFIGURATIONLISTENER_ON_CONFIGURATION_SUCCESS,
// onAfterClass
ICLASSLISTENER_ON_AFTER_CLASS,
IEXECUTION_VISUALISER_CONSUME_DOT_DEFINITION,
ITESTLISTENER_ON_FINISH_TEST_TAG,
ISUITELISTENER_ON_FINISH,
IREPORTER_GENERATE_REPORT,
IEXECUTIONLISTENER_ON_EXECUTION_FINISH);

try {
System.setProperty(RuntimeBehavior.SYMMETRIC_LISTENER_EXECUTION, Boolean.TRUE.toString());
runTest(symmetricExpected, SimpleTestClassWithBeforeAndAfterClass.class);
} finally {
System.setProperty(RuntimeBehavior.SYMMETRIC_LISTENER_EXECUTION, Boolean.FALSE.toString());
}
}

private static void runTest(List<String> expected, Class<?> clazz) {
runTest(expected, clazz, false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package test.listeners.ordering;

import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class SimpleTestClassWithBeforeAndAfterClass {
@BeforeClass
public void beforeClass() {}

@Test
public void testWillPass() {}

@AfterClass
public void afterClass() {}
}

0 comments on commit 8634720

Please sign in to comment.