Skip to content

Commit e8f17fe

Browse files
authored
Allow ITestObjectFactory injection via listeners (#2677)
Closes #2676
1 parent db17f3c commit e8f17fe

File tree

13 files changed

+153
-82
lines changed

13 files changed

+153
-82
lines changed

CHANGES.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Current
2+
Fixed: GITHUB-2676: NPE is triggered when working with ITestObjectFactory (Krishnan Mahadevan)
23
Fixed: GITHUB-2674: Run onTestSkipped for each value from data provider (Krishnan Mahadevan)
34
Fixed: GITHUB-2672: Log real stacktrace when test times out. (cdalexndr)
45
Fixed: GITHUB-2669: A failed retry with ITestContext will lose the ITestContext. (Nan Liang)

testng-core/src/main/java/org/testng/SuiteRunner.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ private void init(
155155
!configuration.getObjectFactory().getClass().equals(suite.getObjectFactoryClass());
156156
final ITestObjectFactory suiteObjectFactory;
157157
if (create) {
158+
if (objectFactory == null) {
159+
objectFactory = configuration.getObjectFactory();
160+
}
158161
// Dont keep creating the object factory repeatedly since our current object factory
159162
// Was already created based off of a suite level object factory.
160163
suiteObjectFactory = objectFactory.newInstance(suite.getObjectFactoryClass());
@@ -166,27 +169,27 @@ private void init(
166169
@Override
167170
public <T> T newInstance(Class<T> cls, Object... parameters) {
168171
try {
169-
return configuration.getObjectFactory().newInstance(cls, parameters);
170-
} catch (Exception e) {
171172
return suiteObjectFactory.newInstance(cls, parameters);
173+
} catch (Exception e) {
174+
return configuration.getObjectFactory().newInstance(cls, parameters);
172175
}
173176
}
174177

175178
@Override
176179
public <T> T newInstance(String clsName, Object... parameters) {
177180
try {
178-
return configuration.getObjectFactory().newInstance(clsName, parameters);
179-
} catch (Exception e) {
180181
return suiteObjectFactory.newInstance(clsName, parameters);
182+
} catch (Exception e) {
183+
return configuration.getObjectFactory().newInstance(clsName, parameters);
181184
}
182185
}
183186

184187
@Override
185188
public <T> T newInstance(Constructor<T> constructor, Object... parameters) {
186189
try {
187-
return configuration.getObjectFactory().newInstance(constructor, parameters);
188-
} catch (Exception e) {
189190
return suiteObjectFactory.newInstance(constructor, parameters);
191+
} catch (Exception e) {
192+
return configuration.getObjectFactory().newInstance(constructor, parameters);
190193
}
191194
}
192195
};

testng-core/src/main/java/org/testng/TestNG.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.testng.internal.annotations.JDK15AnnotationFinder;
4040
import org.testng.internal.invokers.SuiteRunnerMap;
4141
import org.testng.internal.invokers.objects.GuiceContext;
42+
import org.testng.internal.objects.DefaultTestObjectFactory;
4243
import org.testng.internal.objects.Dispenser;
4344
import org.testng.internal.objects.IObjectDispenser;
4445
import org.testng.internal.objects.pojo.BasicAttributes;
@@ -169,7 +170,7 @@ public class TestNG {
169170

170171
private final Set<XmlMethodSelector> m_selectors = Sets.newLinkedHashSet();
171172

172-
private static final ITestObjectFactory DEFAULT_OBJECT_FACTORY = new ITestObjectFactory() {};
173+
private static final ITestObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultTestObjectFactory();
173174
private ITestObjectFactory m_objectFactory = DEFAULT_OBJECT_FACTORY;
174175

175176
private final Map<Class<? extends IInvokedMethodListener>, IInvokedMethodListener>

testng-core/src/main/java/org/testng/TestRunner.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ private void init(
240240
m_host = suite.getHost();
241241
m_testClassesFromXml = test.getXmlClasses();
242242
m_injectorFactory = m_configuration.getInjectorFactory();
243-
m_objectFactory = m_configuration.getObjectFactory();
243+
m_objectFactory = suite.getObjectFactory();
244244
setVerbose(test.getVerbose());
245245

246246
boolean preserveOrder = test.getPreserveOrder();

testng-core/src/main/java/org/testng/internal/ClassImpl.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.testng.collections.Lists;
1111
import org.testng.collections.Objects;
1212
import org.testng.internal.annotations.IAnnotationFinder;
13+
import org.testng.internal.objects.DefaultTestObjectFactory;
1314
import org.testng.internal.objects.Dispenser;
1415
import org.testng.internal.objects.IObjectDispenser;
1516
import org.testng.internal.objects.pojo.BasicAttributes;
@@ -94,7 +95,11 @@ private Object getDefaultInstance(boolean create, String errMsgPrefix) {
9495
if (m_instance != null) {
9596
m_defaultInstance = m_instance;
9697
} else {
97-
IObjectDispenser dispenser = Dispenser.newInstance(m_objectFactory);
98+
ITestObjectFactory factory = m_objectFactory;
99+
if (factory instanceof DefaultTestObjectFactory) {
100+
factory = m_testContext.getSuite().getObjectFactory();
101+
}
102+
IObjectDispenser dispenser = Dispenser.newInstance(factory);
98103
BasicAttributes basic = new BasicAttributes(this, null);
99104
DetailedAttributes detailed = newDetailedAttributes(create, errMsgPrefix);
100105
CreationAttributes attributes = new CreationAttributes(m_testContext, basic, detailed);
@@ -118,7 +123,11 @@ public Object[] getInstances(boolean create, String errorMsgPrefix) {
118123
if (create) {
119124
DetailedAttributes ea = newDetailedAttributes(create, errorMsgPrefix);
120125
CreationAttributes attributes = new CreationAttributes(m_testContext, null, ea);
121-
result = new Object[] {Dispenser.newInstance(m_objectFactory).dispense(attributes)};
126+
result =
127+
new Object[] {
128+
Dispenser.newInstance(m_testContext.getSuite().getObjectFactory())
129+
.dispense(attributes)
130+
};
122131
}
123132
}
124133
if (m_instances.size() > 0) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.testng.internal.objects;
2+
3+
import org.testng.ITestObjectFactory;
4+
5+
/**
6+
* Intended to be the default way of instantiating objects within TestNG. Intentionally does not
7+
* provide any specific implementation because the interface already defines the default behavior.
8+
* This class still exists to ensure that we dont use an anonymous object instantiation so that its
9+
* easy to find out what type of object factory is being injected into our object dispensing
10+
* mechanisms.
11+
*/
12+
public class DefaultTestObjectFactory implements ITestObjectFactory {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package test.objectfactory;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.testng.TestNG;
6+
import org.testng.TestNGException;
7+
import org.testng.annotations.DataProvider;
8+
import org.testng.annotations.Test;
9+
import org.testng.xml.XmlSuite;
10+
import test.SimpleBaseTest;
11+
import test.objectfactory.github1131.EmptyConstructorSample;
12+
import test.objectfactory.github1131.IntConstructorSample;
13+
import test.objectfactory.github1131.MyObjectFactory;
14+
import test.objectfactory.github1131.StringConstructorSample;
15+
import test.objectfactory.github1827.GitHub1827Sample;
16+
import test.objectfactory.issue2676.LocalSuiteAlteringListener;
17+
import test.objectfactory.issue2676.LoggingObjectFactorySample;
18+
import test.objectfactory.issue2676.TestClassSample;
19+
20+
public class ObjectFactoryTest extends SimpleBaseTest {
21+
22+
@Test(description = "GITHUB-2676")
23+
public void ensureObjectFactoryIsInvokedWhenAddedViaListeners() {
24+
TestNG testng = create(TestClassSample.class);
25+
testng.addListener(new LocalSuiteAlteringListener());
26+
testng.run();
27+
assertThat(LoggingObjectFactorySample.wasInvoked).isTrue();
28+
}
29+
30+
@Test(
31+
expectedExceptions = TestNGException.class,
32+
expectedExceptionsMessageRegExp = ".*Check to make sure it can be instantiated",
33+
description = "GITHUB-1827")
34+
public void ensureExceptionThrownWhenNoSuitableConstructorFound() {
35+
36+
TestNG testng = create(GitHub1827Sample.class);
37+
testng.run();
38+
}
39+
40+
@Test(dataProvider = "dp", description = "GITHUB-1131")
41+
public void factoryWithEmptyConstructorShouldWork(boolean bool) {
42+
testFactory(bool, EmptyConstructorSample.class);
43+
assertThat(MyObjectFactory.allParams).containsExactly(new Object[] {}, new Object[] {});
44+
}
45+
46+
@Test(dataProvider = "dp", description = "GITHUB-1131")
47+
public void factoryWithIntConstructorShouldWork(boolean bool) {
48+
testFactory(bool, IntConstructorSample.class);
49+
assertThat(MyObjectFactory.allParams).containsExactly(new Object[] {1}, new Object[] {2});
50+
}
51+
52+
@Test(dataProvider = "dp", description = "GITHUB-1131")
53+
public void factoryWithStringConstructorShouldWork(boolean bool) {
54+
testFactory(bool, StringConstructorSample.class);
55+
assertThat(MyObjectFactory.allParams)
56+
.containsExactly(new Object[] {"foo"}, new Object[] {"bar"});
57+
}
58+
59+
private void testFactory(boolean onSuite, Class<?> sample) {
60+
MyObjectFactory.allParams.clear();
61+
62+
XmlSuite suite = createXmlSuite("Test IObjectFactory2", "TmpTest", sample);
63+
TestNG tng = create(suite);
64+
65+
if (onSuite) {
66+
suite.setObjectFactoryClass(MyObjectFactory.class);
67+
} else {
68+
tng.setObjectFactory(MyObjectFactory.class);
69+
}
70+
71+
tng.run();
72+
}
73+
74+
@DataProvider
75+
public static Object[][] dp() {
76+
return new Object[][] {new Object[] {true}, new Object[] {false}};
77+
}
78+
}

testng-core/src/test/java/test/objectfactory/github1131/GitHub1131Test.java

-51
This file was deleted.

testng-core/src/test/java/test/objectfactory/github1827/GitHub1827Test.java

-19
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.objectfactory.issue2676;
2+
3+
import java.util.List;
4+
import org.testng.IAlterSuiteListener;
5+
import org.testng.xml.XmlSuite;
6+
7+
public class LocalSuiteAlteringListener implements IAlterSuiteListener {
8+
9+
@Override
10+
public void alter(List<XmlSuite> suites) {
11+
suites.forEach(each -> each.setObjectFactoryClass(LoggingObjectFactorySample.class));
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package test.objectfactory.issue2676;
2+
3+
import java.lang.reflect.Constructor;
4+
import org.testng.ITestObjectFactory;
5+
import org.testng.internal.objects.InstanceCreator;
6+
7+
public class LoggingObjectFactorySample implements ITestObjectFactory {
8+
9+
public static boolean wasInvoked = false;
10+
11+
@Override
12+
public <T> T newInstance(Constructor<T> constructor, Object... parameters) {
13+
wasInvoked = true;
14+
return InstanceCreator.newInstance(constructor, parameters);
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package test.objectfactory.issue2676;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class TestClassSample {
6+
7+
@Test
8+
public void sampleTestMethod() {}
9+
}

testng-core/src/test/resources/testng.xml

+1-2
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
<class name="test.factory.issue1924.IssueTest"/>
208208
<class name="test.factory.github328.GitHub328Test" />
209209
<class name="test.factory.issue1745.Github1745Test"/>
210-
<class name="test.objectfactory.github1827.GitHub1827Test"/>
210+
<class name="test.objectfactory.ObjectFactoryTest"/>
211211
<class name="test.github1490.VerifyDataProviderListener"/>
212212
<class name="test.listeners.issue2456.IssueTest"/>
213213
<class name="test.methodselection.MethodSelectionTest"/>
@@ -432,7 +432,6 @@
432432
<class name="test.factory.FactoryIntegrationTest" />
433433
<class name="test.factory.EmptyFactoryDataProviderTest" />
434434

435-
<class name="test.factory.github1131.GitHub1131Test" />
436435
<class name="test.factory.nested.GitHub1307Test"/>
437436

438437
</classes>

0 commit comments

Comments
 (0)