Skip to content

Commit aae440a

Browse files
committed
Allow users to access factory method params info
Closes #3111 TestNG now exposes the IParameterInfo interface Via which you can extract the following details Pertaining to a factory powered test. * the index - which would match with what was Specified in the “indices” attribute of the “Factory” Annotation. If nothing was specified, then this Would be equal to a running count on the total instances. * current index - which represents a running count On the total instances. * The parameters of the factory method * The instance that was produced.
1 parent 249ea08 commit aae440a

14 files changed

+226
-35
lines changed

CHANGES.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Current (7.11.0)
2+
Fixed: GITHUB-3111: Replacement API for IClass.getInstanceHashCodes() and IClass.getInstances(boolean) (Krishnan Mahadevan)
23

34
7.10.1
45
Fixed: GITHUB-3110: Update from testng 7.9.0 to 7.10.0 break maven build with junit5 (Krishnan Mahadevan)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.testng;
2+
3+
/** Represents the ability to retrieve the parameters associated with a factory method. */
4+
public interface IParameterInfo {
5+
6+
/** @return - The actual instance associated with a factory method */
7+
Object getInstance();
8+
9+
/**
10+
* @return - The actual index of instance associated with a factory method. This index has a 1:1
11+
* correspondence with what were specified via the <code>indices</code> attribute of the
12+
* <code>@Factory</code> annotation. For e.g., lets say you specified the
13+
* indices to the "1" and your factory returned 4 instances, then the instance on which this
14+
* method is invoked would have the value as "1".
15+
*/
16+
int getIndex();
17+
18+
/**
19+
* @return - returns an index which indicates the running position in the array of test class
20+
* instances that were produced by a <code>@Factory</code> annotated constructor or static
21+
* method. For e.g., lets say your <code>@Factory</code> method returned 4 instances, then
22+
* each of the invocations to this method would return a value from <code>0</code> to <code>3
23+
* </code>
24+
*/
25+
int currentIndex();
26+
27+
/** @return - The parameters associated with the factory method as an array. */
28+
Object[] getParameters();
29+
30+
static Object embeddedInstance(Object original) {
31+
if (original instanceof IParameterInfo) {
32+
return ((IParameterInfo) original).getInstance();
33+
}
34+
return original;
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
11
package org.testng.internal;
22

3-
/** Represents the ability to retrieve the parameters associated with a factory method. */
4-
public interface IParameterInfo {
5-
6-
/** @return - The actual instance associated with a factory method */
7-
Object getInstance();
8-
9-
/** @return - The actual index of instance associated with a factory method */
10-
int getIndex();
11-
12-
/** @return - The parameters associated with the factory method as an array. */
13-
Object[] getParameters();
14-
15-
static Object embeddedInstance(Object original) {
16-
if (original instanceof IParameterInfo) {
17-
return ((IParameterInfo) original).getInstance();
18-
}
19-
return original;
20-
}
21-
}
3+
/**
4+
* Represents the ability to retrieve the parameters associated with a factory method.
5+
*
6+
* @deprecated - This interface stands deprecated as of TestNG <code>7.11.0</code>.
7+
*/
8+
@Deprecated
9+
public interface IParameterInfo extends org.testng.IParameterInfo {}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public String getMethodName() {
153153
public Object getInstance() {
154154
return Optional.ofNullable(m_instance)
155155
.map(IObject.IdentifiableObject::getInstance)
156-
.map(IParameterInfo::embeddedInstance)
156+
.map(org.testng.IParameterInfo::embeddedInstance)
157157
.orElse(null);
158158
}
159159

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public void addInstance(Object instance) {
162162
}
163163

164164
private static int computeHashCode(Object instance) {
165-
return IParameterInfo.embeddedInstance(instance).hashCode();
165+
return org.testng.IParameterInfo.embeddedInstance(instance).hashCode();
166166
}
167167

168168
private DetailedAttributes newDetailedAttributes(boolean create, String errMsgPrefix) {

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

+13-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.List;
88
import java.util.Map;
99
import java.util.Set;
10+
import java.util.concurrent.atomic.AtomicInteger;
1011
import java.util.stream.Collectors;
1112
import org.testng.DataProviderHolder;
1213
import org.testng.IDataProviderInterceptor;
@@ -144,8 +145,8 @@ private static String[] getAllGroups(
144145
return groups.toArray(new String[0]);
145146
}
146147

147-
public IParameterInfo[] invoke() {
148-
List<IParameterInfo> result = Lists.newArrayList();
148+
public org.testng.IParameterInfo[] invoke() {
149+
List<org.testng.IParameterInfo> result = Lists.newArrayList();
149150

150151
Map<String, String> allParameterNames = Maps.newHashMap();
151152
Parameters.MethodParameters methodParameters =
@@ -174,6 +175,7 @@ public IParameterInfo[] invoke() {
174175
try {
175176
List<Integer> indices = factoryAnnotation.getIndices();
176177
int position = 0;
178+
AtomicInteger counter = new AtomicInteger(0);
177179
while (parameterIterator.hasNext()) {
178180
Object[] parameters = parameterIterator.next();
179181
if (parameters == null) {
@@ -196,21 +198,27 @@ public IParameterInfo[] invoke() {
196198
final int instancePosition = position;
197199
result.addAll(
198200
Arrays.stream(testInstances)
199-
.map(instance -> new ParameterInfo(instance, instancePosition, parameters))
201+
.map(
202+
instance ->
203+
new ParameterInfo(
204+
instance, instancePosition, parameters, counter.getAndIncrement()))
200205
.collect(Collectors.toList()));
201206
} else {
202207
for (Integer index : indices) {
203208
int i = index - position;
204209
if (i >= 0 && i < testInstances.length) {
205-
result.add(new ParameterInfo(testInstances[i], position, parameters));
210+
result.add(
211+
new ParameterInfo(
212+
testInstances[i], position, parameters, counter.getAndIncrement()));
206213
}
207214
}
208215
}
209216
position += testInstances.length;
210217
} else {
211218
if (indices == null || indices.isEmpty() || indices.contains(position)) {
212219
Object instance = m_objectFactory.newInstance(com.getConstructor(), parameters);
213-
result.add(new ParameterInfo(instance, position, parameters));
220+
result.add(
221+
new ParameterInfo(instance, position, parameters, counter.getAndIncrement()));
214222
}
215223
position++;
216224
}

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

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package org.testng.internal;
22

33
public class ParameterInfo implements IParameterInfo {
4-
private Object instance;
4+
private final Object instance;
55
private final int index;
6-
private Object[] parameters;
6+
private final Object[] parameters;
7+
private final int currentIndex;
78

8-
public ParameterInfo(Object instance, int index, Object[] parameters) {
9+
public ParameterInfo(Object instance, int index, Object[] parameters, int currentIndex) {
910
this.instance = instance;
1011
this.index = index;
1112
this.parameters = parameters;
13+
this.currentIndex = currentIndex;
1214
}
1315

1416
@Override
@@ -21,6 +23,11 @@ public int getIndex() {
2123
return index;
2224
}
2325

26+
@Override
27+
public int currentIndex() {
28+
return currentIndex;
29+
}
30+
2431
@Override
2532
public Object[] getParameters() {
2633
return parameters;

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ private ClassInfoMap processFactory(IClass ic, ConstructorOrMethod factoryMethod
178178
// If the factory returned IInstanceInfo, get the class from it,
179179
// otherwise, just call getClass() on the returned instances
180180
int i = 0;
181-
for (IParameterInfo o : fm.invoke()) {
181+
for (org.testng.IParameterInfo o : fm.invoke()) {
182182
if (o == null) {
183183
throw new TestNGException(
184184
"The factory " + fm + " returned a null instance" + "at index " + i);
@@ -329,8 +329,8 @@ private <T> void addInstance(IInstanceInfo<T> ii) {
329329

330330
private void addInstance(IObject.IdentifiableObject o) {
331331
Class<?> key = o.getInstance().getClass();
332-
if (o.getInstance() instanceof IParameterInfo) {
333-
key = ((IParameterInfo) o.getInstance()).getInstance().getClass();
332+
if (o.getInstance() instanceof org.testng.IParameterInfo) {
333+
key = ((org.testng.IParameterInfo) o.getInstance()).getInstance().getClass();
334334
}
335335
addInstance(key, o);
336336
}

testng-core/src/test/java/test/factory/FactoryIntegrationTest.java

+43-2
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,24 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
55

6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.stream.Collectors;
69
import org.testng.Assert;
10+
import org.testng.IParameterInfo;
11+
import org.testng.ITestListener;
12+
import org.testng.ITestResult;
713
import org.testng.TestListenerAdapter;
814
import org.testng.TestNG;
915
import org.testng.TestNGException;
16+
import org.testng.annotations.DataProvider;
1017
import org.testng.annotations.Test;
1118
import test.InvokedMethodNameListener;
1219
import test.SimpleBaseTest;
20+
import test.factory.issue3111.SimpleFactoryPoweredTestSample;
21+
import test.factory.issue3111.SimpleFactoryPoweredTestWithIndicesSample;
22+
import test.factory.issue3111.SimpleFactoryPoweredTestWithoutDataProviderSample;
23+
import test.factory.issue3111.SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample;
1324

1425
public class FactoryIntegrationTest extends SimpleBaseTest {
1526

@@ -22,7 +33,9 @@ public void testExceptionWithNonStaticFactoryMethod() {
2233
} catch (TestNGException e) {
2334
assertThat(e)
2435
.hasMessage(
25-
"\nCan't invoke public java.lang.Object[] test.factory.GitHub876Sample.createInstances(): either make it static or add a no-args constructor to your class");
36+
"\nCan't invoke public java.lang.Object[] test.factory.GitHub876Sample"
37+
+ ".createInstances(): either make it static or add a no-args constructor to "
38+
+ "your class");
2639
}
2740
}
2841

@@ -46,7 +59,8 @@ public void testExceptionWithBadFactoryMethodReturnType() {
4659
} catch (TestNGException e) {
4760
assertThat(e)
4861
.hasMessage(
49-
"\ntest.factory.BadMethodReturnTypeFactory.createInstances MUST return [ java.lang.Object[] or org.testng.IInstanceInfo[] ] but returns java.lang.Object");
62+
"\ntest.factory.BadMethodReturnTypeFactory.createInstances MUST return [ java.lang"
63+
+ ".Object[] or org.testng.IInstanceInfo[] ] but returns java.lang.Object");
5064
}
5165
}
5266

@@ -65,4 +79,31 @@ public void doubleFactoryMethodShouldWork() {
6579
"FactoryBaseSample{1}#f",
6680
"FactoryBaseSample{2}#f", "FactoryBaseSample{3}#f", "FactoryBaseSample{4}#f");
6781
}
82+
83+
@Test(dataProvider = "testdata", description = "GITHUB-3111")
84+
public void ensureCurrentIndexWorksForFactoryPoweredTests(Class<?> klass, Integer[] expected) {
85+
List<IParameterInfo> params = new ArrayList<>();
86+
TestNG testng = create(klass);
87+
testng.addListener(
88+
new ITestListener() {
89+
@Override
90+
public void onTestSuccess(ITestResult result) {
91+
params.add(result.getMethod().getFactoryMethodParamsInfo());
92+
}
93+
});
94+
testng.run();
95+
List<Integer> actualIndices =
96+
params.stream().map(IParameterInfo::currentIndex).sorted().collect(Collectors.toList());
97+
assertThat(actualIndices).containsExactly(expected);
98+
}
99+
100+
@DataProvider(name = "testdata")
101+
public Object[][] testdata() {
102+
return new Object[][] {
103+
{SimpleFactoryPoweredTestSample.class, new Integer[] {0, 1, 2}},
104+
{SimpleFactoryPoweredTestWithIndicesSample.class, new Integer[] {0}},
105+
{SimpleFactoryPoweredTestWithoutDataProviderSample.class, new Integer[] {0, 1, 2}},
106+
{SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample.class, new Integer[] {0}},
107+
};
108+
}
68109
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package test.factory.issue3111;
2+
3+
import org.testng.Reporter;
4+
import org.testng.annotations.DataProvider;
5+
import org.testng.annotations.Factory;
6+
import org.testng.annotations.Test;
7+
8+
public class SimpleFactoryPoweredTestSample {
9+
10+
private final int i;
11+
12+
@Factory(dataProvider = "data")
13+
public SimpleFactoryPoweredTestSample(int i) {
14+
this.i = i;
15+
}
16+
17+
@Test
18+
public void test() {
19+
Reporter.log(Integer.toString(i));
20+
}
21+
22+
@DataProvider
23+
public static Object[][] data() {
24+
return new Object[][] {{1}, {2}, {3}};
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package test.factory.issue3111;
2+
3+
import org.testng.Reporter;
4+
import org.testng.annotations.DataProvider;
5+
import org.testng.annotations.Factory;
6+
import org.testng.annotations.Test;
7+
8+
public class SimpleFactoryPoweredTestWithIndicesSample {
9+
10+
private final int i;
11+
12+
@Factory(
13+
dataProvider = "data",
14+
indices = {1})
15+
public SimpleFactoryPoweredTestWithIndicesSample(int i) {
16+
this.i = i;
17+
}
18+
19+
@Test
20+
public void test() {
21+
Reporter.log(Integer.toString(i));
22+
}
23+
24+
@DataProvider
25+
public static Object[][] data() {
26+
return new Object[][] {{1}, {2}, {3}};
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package test.factory.issue3111;
2+
3+
import org.testng.Reporter;
4+
import org.testng.annotations.Factory;
5+
import org.testng.annotations.Test;
6+
7+
public class SimpleFactoryPoweredTestWithoutDataProviderSample {
8+
9+
private final int i;
10+
11+
public SimpleFactoryPoweredTestWithoutDataProviderSample(int i) {
12+
this.i = i;
13+
}
14+
15+
@Test
16+
public void test() {
17+
Reporter.log(Integer.toString(i));
18+
}
19+
20+
@Factory
21+
public static Object[] data() {
22+
return new Object[] {
23+
new SimpleFactoryPoweredTestWithoutDataProviderSample(1),
24+
new SimpleFactoryPoweredTestWithoutDataProviderSample(2),
25+
new SimpleFactoryPoweredTestWithoutDataProviderSample(3),
26+
};
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package test.factory.issue3111;
2+
3+
import org.testng.Reporter;
4+
import org.testng.annotations.Factory;
5+
import org.testng.annotations.Test;
6+
7+
public class SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample {
8+
9+
private final int i;
10+
11+
public SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(int i) {
12+
this.i = i;
13+
}
14+
15+
@Test
16+
public void test() {
17+
Reporter.log(Integer.toString(i));
18+
}
19+
20+
@Factory(indices = {1})
21+
public static Object[] data() {
22+
return new Object[] {
23+
new SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(1),
24+
new SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(2),
25+
new SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(3),
26+
};
27+
}
28+
}

0 commit comments

Comments
 (0)