Skip to content

Commit f634d0a

Browse files
authored
Merge pull request #1630 from krmahadevan/krmahadevan-fix-1625
Null fields in parallel method tests
2 parents c63f594 + fab5688 commit f634d0a

File tree

9 files changed

+121
-24
lines changed

9 files changed

+121
-24
lines changed

CHANGES.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
Current
22
=======
3+
Fixed: GITHUB-1625: Null fields in parallel method tests (Krishnan Mahadevan)
34

4-
6.13
5-
====
5+
6.13.1
6+
No functional changes. Released with newer version JCommander (1.72.0)
67

8+
6.13
79
Fixed: GITHUB-1619: ConcurrentHashMap doesn't secure insertion order.(Yehui Wang)
810
Fixed: GITHUB-1616: Test cases with priority and dependsOnGroups dependencies, execution order is chaos. (Yehui Wang)
911
Fixed: GITHUB-1613: The constructor removed from TestRunner would stop Eclipse working.(Yehui Wang)

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ dependencies {
8989
testCompile 'org.assertj:assertj-core:2.5.0'
9090
testCompile 'org.codehaus.groovy:groovy-all:2.4.7'
9191
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
92+
testCompile 'org.mockito:mockito-core:2.12.0'
9293
}
9394

9495
task sourceJar(type: Jar) {

kobalt/src/Build.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ val p = project {
6464
dependenciesTest {
6565
compile("org.assertj:assertj-core:3.5.2",
6666
"org.testng:testng:6.9.13.7",
67+
"org.mockito:mockito-core:2.12.0",
6768
"org.spockframework:spock-core:1.0-groovy-2.4")
6869
}
6970

src/main/java/org/testng/internal/TestMethodWorker.java

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -148,24 +148,30 @@ protected void invokeBeforeClassMethods(ITestClass testClass, IMethodInstance mi
148148

149149
// the whole invocation must be synchronized as other threads must
150150
// get a full initialized test object (not the same for @After)
151-
Map<ITestClass, Set<Object>> invokedBeforeClassMethods = m_classMethodMap.getInvokedBeforeClassMethods();
152-
Set<Object> instances = invokedBeforeClassMethods.get(testClass);
153-
if (null == instances) {
154-
instances = new HashSet<>();
155-
invokedBeforeClassMethods.put(testClass, instances);
156-
}
157-
Object instance = mi.getInstance();
158-
if (!instances.contains(instance)) {
159-
instances.add(instance);
160-
for (IClassListener listener : m_listeners) {
161-
listener.onBeforeClass(testClass);
151+
// Synchronization on local variables is generally considered a bad practice, but this is an exception.
152+
// We need to ensure that two threads that are querying for the same "Class" then they
153+
// should be mutually exclusive. In all other cases, parallelism can be allowed.
154+
//DO NOT REMOVE THIS SYNC LOCK.
155+
synchronized (testClass) {
156+
Map<ITestClass, Set<Object>> invokedBeforeClassMethods = m_classMethodMap.getInvokedBeforeClassMethods();
157+
Set<Object> instances = invokedBeforeClassMethods.get(testClass);
158+
if (null == instances) {
159+
instances = new HashSet<>();
160+
invokedBeforeClassMethods.put(testClass, instances);
161+
}
162+
Object instance = mi.getInstance();
163+
if (!instances.contains(instance)) {
164+
instances.add(instance);
165+
for (IClassListener listener : m_listeners) {
166+
listener.onBeforeClass(testClass);
167+
}
168+
m_invoker.invokeConfigurations(testClass,
169+
testClass.getBeforeClassMethods(),
170+
m_suite,
171+
m_parameters,
172+
null, /* no parameter values */
173+
instance);
162174
}
163-
m_invoker.invokeConfigurations(testClass,
164-
testClass.getBeforeClassMethods(),
165-
m_suite,
166-
m_parameters,
167-
null, /* no parameter values */
168-
instance);
169175
}
170176
}
171177

src/test/java/test/configuration/BeforeClassThreadTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55
import org.testng.xml.XmlSuite;
66

77
import test.SimpleBaseTest;
8-
import junit.framework.Assert;
98

10-
public class BeforeClassThreadTest extends SimpleBaseTest{
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
public class BeforeClassThreadTest extends SimpleBaseTest {
1112

1213
@Test
1314
public void beforeClassMethodsShouldRunInParallel() {
14-
TestNG tng = create(new Class[] { BeforeClassThreadA.class, BeforeClassThreadB.class });
15+
TestNG tng = create(BeforeClassThreadA.class, BeforeClassThreadB.class);
1516
tng.setParallel(XmlSuite.ParallelMode.METHODS);
1617
tng.run();
17-
18-
Assert.assertTrue(Math.abs(BeforeClassThreadA.WHEN - BeforeClassThreadB.WHEN) < 1000);
18+
assertThat(Math.abs(BeforeClassThreadA.WHEN - BeforeClassThreadB.WHEN)).isLessThan(1000);
1919
}
2020
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package test.configuration.github1625;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.testng.TestNG;
6+
import org.testng.annotations.DataProvider;
7+
import org.testng.annotations.Test;
8+
import org.testng.xml.XmlSuite;
9+
import test.SimpleBaseTest;
10+
11+
public class TestRunnerIssue1625 extends SimpleBaseTest {
12+
13+
@Test(dataProvider = "dp")
14+
public void testMethod(Class<?> clazz) {
15+
TestNG testNG = create(clazz);
16+
testNG.setParallel(XmlSuite.ParallelMode.METHODS);
17+
testNG.run();
18+
assertThat(testNG.getStatus()).isEqualTo(0);
19+
}
20+
21+
@DataProvider(name = "dp")
22+
public Object[][] getData() {
23+
return new Object[][]{
24+
{TestclassSampleUsingMocks.class},
25+
{TestclassSampleWithoutUsingMocks.class}
26+
};
27+
}
28+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package test.configuration.github1625;
2+
3+
import org.mockito.Mock;
4+
import org.mockito.MockitoAnnotations;
5+
import org.testng.Assert;
6+
import org.testng.annotations.BeforeClass;
7+
import org.testng.annotations.Test;
8+
9+
import java.util.List;
10+
11+
public class TestclassSampleUsingMocks {
12+
@Mock
13+
List<String> list;
14+
15+
16+
@BeforeClass
17+
public void beforeClass() {
18+
MockitoAnnotations.initMocks(this);
19+
}
20+
21+
@Test
22+
public void first() {
23+
Assert.assertNotNull(list);
24+
}
25+
26+
@Test
27+
public void second() {
28+
Assert.assertNotNull(list);
29+
}
30+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package test.configuration.github1625;
2+
3+
import org.testng.Assert;
4+
import org.testng.annotations.BeforeClass;
5+
import org.testng.annotations.Test;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
public class TestclassSampleWithoutUsingMocks {
11+
12+
private List<String> list;
13+
14+
@BeforeClass
15+
public void beforeClass() {
16+
list = new ArrayList<>();
17+
}
18+
19+
@Test
20+
public void first() {
21+
Assert.assertNotNull(list);
22+
}
23+
24+
@Test
25+
public void second() {
26+
Assert.assertNotNull(list);
27+
}
28+
}

src/test/resources/testng.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,7 @@
782782
<class name="test.configuration.VerifySuiteTest" />
783783
<class name="test.configuration.SingleConfigurationTest" />
784784
<class name="test.configuration.BeforeClassWithDisabledTest" />
785+
<class name="test.configuration.github1625.TestRunnerIssue1625"/>
785786
</classes>
786787
</test>
787788

0 commit comments

Comments
 (0)