Before I do the work to set up a bug report, I wanted to run an issue by the developers:
A while back, I was struggling with setting up the tests globally to run with 'parallel=class'. I received some help at
https://groups.google.com/g/testng-users/c/0bSMJo62ZqcMy solution was to write a file at META-INF/services/org.testing.ITestNGListener with a single line "com.mri.testng.listener.TestNgSuiteListener". That class is:
```
public class TestNgSuiteListener implements ISuiteListener {
@Override
public void onStart(ISuite suite) {
Object parallel = suite.getAttribute("parallel");
if (EqualUtils.equals(parallel, "classes")) {
return;
}
\\LogManager.getLogger(TestNgSuiteListener.class).info("changing suite={} parallel={} to parallel=classes", suite.getName(), parallel);
suite.setAttribute("parallel", "classes");
}
}
```
And it has worked very nicely. Thank you for the previous advice.
I have a convenience abstract superclass test class that lets me easily switch between "setup per class" and "setup per method".
(This class does a lot more environmental setup work which I am not showing.)I am just showing this for setup
```
public abstract class AbstractAppTest {
// either per class or per method
protected abstract boolean isSetupPerClass();
@BeforeClass(alwaysRun = true)
public final void beforeClass() {
if (isSetupPerClass()) {
System.out.println("setupHook-beforeClass="+getClass() + " thread="+Thread.currentThread());
setupHook();
}
beforeClassHook();
}
@BeforeMethod(alwaysRun = true)
public final void beforeMethod() {
if (!isSetupPerClass()) {
System.out.println("setupHook-beforeMethod="+getClass() + " thread="+Thread.currentThread());
setupHook();
}
beforeMethodHook();
}
@AfterClass(alwaysRun = true)
public final void afterClass() {
try {
afterClassHook();
} catch (Exception e) {
e.printStackTrace();
}
if (isSetupPerClass()) {
System.out.println("tearDownHook-afterClass="+getClass() + " thread="+Thread.currentThread());
tearDownHook();
}
}
@AfterMethod(alwaysRun = true)
public final void afterMethod() {
try {
afterMethodHook();
} catch (Exception e) {
e.printStackTrace();
}
if (!isSetupPerClass()) {
System.out.println("tearDownHook-afterMethod="+getClass() + " thread="+Thread.currentThread());
tearDownHook();
}
}
protected void beforeClassHook() {}
protected void afterClassHook() {}
protected void beforeMethodHook() {}
protected void afterMethodHook() {}
protected void setupHook() {}
protected void tearDownHook() {}
}
````
And then the following subclass:
```
public class ChildClass_Test extends AbstractAppTest {
@Override
protected boolean isSetupPerClass() {
return true;
}
//many test methods
public static class Grandchild_Test extends ChildClass_Test {
//a few overrides for different setup, but all the tests are inherited from ChildClass_Test
//Basically, I have a class hierarchy under test, and so a separate test class for each.
//I made this a static inner class because it is very minimal. It was an arbitrary decision.
}
}
```
The problem is that all the tests in ChildClass_Test succeed and all the tests for Grandchild_Test fail.
My app is pre-alpha with still many issues with ThreadLocals, so I have been franctically trying to figure out my bug.
Except that I finally wrote logging to show me the setup/tearDown activity, along with the Thread information. This is the System.out.println code you see in the AbstractAppTest.
And this is what I see in the console. Note that everything is running in a single thread. I am not actually using a testng.xml file here, where I would normally specify multiple threads.
```
setupHook-beforeClass=class Grandchild_Test thread=Thread[#1,main,5,main]
setupHook-beforeClass=class ChildClass_Test thread=Thread[#1,main,5,main]
tearDownHook-afterClass=class Grandchild_Test thread=Thread[#1,main,5,main]
tearDownHook-afterClass=class ChildClass_Test thread=Thread[#1,main,5,main]
```
Note that both classes start setup in the same thread, and run interleaved. This is the reason that my tests break.
After tearing much hair (i.e., before I was smart enough to add logging), I moved the Grandchild_Test class to its own separate file. And that was the fix. The test classes now run sequentially rather than simultaneously, and everything is fine.
So it seems to me that something in the testng code does not handle (1) 'parallel-classes' in a (2) single thread when (3) some classes are static inner classes.