Skip to content

Commit 7070b02

Browse files
committed
Streamline dependsOnMethods for configurations
Closes #550
1 parent d7e0bb1 commit 7070b02

9 files changed

+229
-1
lines changed

CHANGES.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Current
22
Fixed: GITHUB-2844: Deprecate support for running Spock Tests (Krishnan Mahadevan)
3+
Fixed: GITHUB-550: Weird @BeforeMethod and @AfterMethod behaviour with dependsOnMethods (Krishnan Mahadevan)
34
Fixed: GITHUB-893: TestNG should provide an Api which allow to find all dependent of a specific test (Krishnan Mahadevan)
45
New: Added .yml file extension for yaml suite files, previously only .yaml was allowed for yaml (Steven Jubb)
56
Fixed: GITHUB-141: regular expression in "dependsOnMethods" does not work (Krishnan Mahadevan)

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

+61-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.Set;
1111
import java.util.concurrent.ConcurrentHashMap;
1212
import java.util.concurrent.atomic.AtomicReference;
13+
import java.util.function.Predicate;
1314
import java.util.regex.Pattern;
1415
import java.util.stream.Collectors;
1516
import java.util.stream.Stream;
@@ -90,6 +91,40 @@ protected static ITestNGMethod[] findDependedUponMethods(
9091
return findDependedUponMethods(m, methodsArray);
9192
}
9293

94+
private static Pair<String, Predicate<ITestNGMethod>> filterToUse(ITestNGMethod m) {
95+
if (m.isBeforeMethodConfiguration()) {
96+
return new Pair<>("BeforeMethod", ITestNGMethod::isBeforeMethodConfiguration);
97+
}
98+
if (m.isAfterMethodConfiguration()) {
99+
return new Pair<>("AfterMethod", ITestNGMethod::isAfterMethodConfiguration);
100+
}
101+
if (m.isBeforeClassConfiguration()) {
102+
return new Pair<>("BeforeClass", ITestNGMethod::isBeforeClassConfiguration);
103+
}
104+
if (m.isAfterClassConfiguration()) {
105+
return new Pair<>("AfterClass", ITestNGMethod::isAfterClassConfiguration);
106+
}
107+
if (m.isBeforeTestConfiguration()) {
108+
return new Pair<>("BeforeTest", ITestNGMethod::isBeforeTestConfiguration);
109+
}
110+
if (m.isAfterTestConfiguration()) {
111+
return new Pair<>("AfterTest", ITestNGMethod::isAfterTestConfiguration);
112+
}
113+
if (m.isBeforeSuiteConfiguration()) {
114+
return new Pair<>("BeforeSuite", ITestNGMethod::isBeforeSuiteConfiguration);
115+
}
116+
if (m.isAfterSuiteConfiguration()) {
117+
return new Pair<>("AfterSuite", ITestNGMethod::isAfterSuiteConfiguration);
118+
}
119+
if (m.isBeforeGroupsConfiguration()) {
120+
return new Pair<>("BeforeGroups", ITestNGMethod::isBeforeGroupsConfiguration);
121+
}
122+
if (m.isAfterGroupsConfiguration()) {
123+
return new Pair<>("AfterGroups", ITestNGMethod::isAfterGroupsConfiguration);
124+
}
125+
return new Pair<>("Test", ITestNGMethod::isTest);
126+
}
127+
93128
/**
94129
* Finds TestNG methods that the specified TestNG method depends upon
95130
*
@@ -104,6 +139,25 @@ public static ITestNGMethod[] findDependedUponMethods(ITestNGMethod m, ITestNGMe
104139
.filter(each -> Objects.isNull(each.getRealClass().getEnclosingClass()))
105140
.toArray(ITestNGMethod[]::new);
106141
String canonicalMethodName = calculateMethodCanonicalName(m);
142+
Pair<String, Predicate<ITestNGMethod>> filterPair = filterToUse(m);
143+
String annotationType = filterPair.first();
144+
Predicate<ITestNGMethod> predicate = filterPair.second();
145+
146+
if (isConfigurationMethod(m)) {
147+
methods =
148+
Arrays.stream(incoming)
149+
.filter(tm -> !tm.equals(m)) // exclude the current config method from the list
150+
.filter(predicate) // include only similar config methods
151+
.toArray(ITestNGMethod[]::new);
152+
153+
if (methods.length == 0) {
154+
String msg =
155+
String.format(
156+
"None of the dependencies of the method %s are annotated with [@%s].",
157+
canonicalMethodName, annotationType);
158+
throw new TestNGException(msg);
159+
}
160+
}
107161

108162
List<ITestNGMethod> vResult = Lists.newArrayList();
109163
String regexp = null;
@@ -142,11 +196,17 @@ public static ITestNGMethod[] findDependedUponMethods(ITestNGMethod m, ITestNGMe
142196
}
143197
Method maybeReferringTo = findMethodByName(m, regexp);
144198
if (maybeReferringTo != null) {
199+
String suffix = " or not included.";
200+
if (isConfigurationMethod(m)) {
201+
suffix = ".";
202+
}
145203
throw new TestNGException(
146204
canonicalMethodName
147205
+ "() is depending on method "
148206
+ maybeReferringTo
149-
+ ", which is not annotated with @Test or not included.");
207+
+ ", which is not annotated with @"
208+
+ annotationType
209+
+ suffix);
150210
}
151211
throw new TestNGException(
152212
canonicalMethodName + "() depends on nonexistent method " + regexp);

testng-core/src/test/java/test/dependent/DependentTest.java

+61
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
import test.dependent.issue141.TestClassSample;
3333
import test.dependent.issue2658.FailingClassSample;
3434
import test.dependent.issue2658.PassingClassSample;
35+
import test.dependent.issue550.ConfigDependencySample;
36+
import test.dependent.issue550.ConfigDependencyWithMismatchedLevelSample;
37+
import test.dependent.issue550.ConfigDependsOnTestAndConfigMethodSample;
38+
import test.dependent.issue550.ConfigDependsOnTestMethodSample;
39+
import test.dependent.issue550.OrderedResultsGatherer;
3540
import test.dependent.issue893.DependencyTrackingListener;
3641
import test.dependent.issue893.MultiLevelDependenciesTestClassSample;
3742

@@ -378,6 +383,62 @@ public Object[][] getUpstreamTestData() {
378383
};
379384
}
380385

386+
@Test(description = "GITHUB-550", dataProvider = "configDependencyTestData")
387+
public void testConfigDependencies(String expectedErrorMsg, Class<?> testClassToUse) {
388+
TestNG testng = create(testClassToUse);
389+
String actualErrorMsg = null;
390+
try {
391+
testng.run();
392+
} catch (TestNGException e) {
393+
actualErrorMsg = e.getMessage().replace("\n", "");
394+
}
395+
assertThat(actualErrorMsg).isEqualTo(expectedErrorMsg);
396+
}
397+
398+
@Test(description = "GITHUB-550")
399+
public void testConfigDependenciesHappyCase() {
400+
TestNG testng = create(ConfigDependencySample.class);
401+
OrderedResultsGatherer gatherer = new OrderedResultsGatherer();
402+
testng.addListener(gatherer);
403+
testng.run();
404+
assertThat(gatherer.getStartTimes()).isSorted();
405+
}
406+
407+
@DataProvider(name = "configDependencyTestData")
408+
public Object[][] configDependencyTestData() {
409+
String template1 = "None of the dependencies of the method %s.%s are annotated with [@%s].";
410+
String template2 =
411+
"%s.%s() is depending on method public void %s.%s(), " + "which is not annotated with @%s.";
412+
return new Object[][] {
413+
{
414+
String.format(
415+
template1,
416+
ConfigDependencyWithMismatchedLevelSample.class.getCanonicalName(),
417+
"beforeMethod",
418+
"BeforeMethod"),
419+
ConfigDependencyWithMismatchedLevelSample.class
420+
},
421+
{
422+
String.format(
423+
template2,
424+
ConfigDependsOnTestAndConfigMethodSample.class.getCanonicalName(),
425+
"beforeMethod",
426+
ConfigDependsOnTestAndConfigMethodSample.class.getCanonicalName(),
427+
"testMethod",
428+
"BeforeMethod"),
429+
ConfigDependsOnTestAndConfigMethodSample.class
430+
},
431+
{
432+
String.format(
433+
template1,
434+
ConfigDependsOnTestMethodSample.class.getCanonicalName(),
435+
"beforeMethod",
436+
"BeforeMethod"),
437+
ConfigDependsOnTestMethodSample.class
438+
}
439+
};
440+
}
441+
381442
public static class MethodNameCollector implements ITestListener {
382443

383444
private static final Function<ITestResult, String> asString =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package test.dependent.issue550;
2+
3+
import java.util.concurrent.TimeUnit;
4+
import org.testng.annotations.BeforeMethod;
5+
import org.testng.annotations.Test;
6+
7+
public class ConfigDependencySample {
8+
9+
@BeforeMethod(dependsOnMethods = "anotherBeforeMethod")
10+
public void beforeMethod() {}
11+
12+
@BeforeMethod
13+
public void anotherBeforeMethod() throws InterruptedException {
14+
TimeUnit.MILLISECONDS.sleep(100);
15+
}
16+
17+
@Test
18+
public void testMethod() {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package test.dependent.issue550;
2+
3+
import org.testng.annotations.BeforeClass;
4+
import org.testng.annotations.BeforeMethod;
5+
import org.testng.annotations.Test;
6+
7+
public class ConfigDependencyWithMismatchedLevelSample {
8+
9+
@BeforeClass
10+
public void beforeClass() {}
11+
12+
@BeforeMethod(dependsOnMethods = "beforeClass")
13+
public void beforeMethod() {}
14+
15+
@Test
16+
public void testMethod() {}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package test.dependent.issue550;
2+
3+
import org.testng.annotations.BeforeClass;
4+
import org.testng.annotations.BeforeMethod;
5+
import org.testng.annotations.Test;
6+
7+
public class ConfigDependsOnTestAndConfigMethodSample {
8+
9+
@BeforeClass
10+
public void beforeClass() {}
11+
12+
@BeforeClass
13+
public void anotherBeforeClass() {}
14+
15+
@BeforeMethod(dependsOnMethods = {"testMethod", "anotherBeforeMethod"})
16+
public void beforeMethod() {}
17+
18+
@BeforeMethod
19+
public void anotherBeforeMethod() {}
20+
21+
@Test
22+
public void testMethod() {}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.dependent.issue550;
2+
3+
import org.testng.annotations.BeforeMethod;
4+
import org.testng.annotations.Test;
5+
6+
public class ConfigDependsOnTestMethodSample {
7+
8+
@BeforeMethod(dependsOnMethods = "testMethod")
9+
public void beforeMethod() {}
10+
11+
@Test
12+
public void testMethod() {}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package test.dependent.issue550;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import org.testng.IInvokedMethod;
6+
import org.testng.IInvokedMethodListener;
7+
import org.testng.ITestResult;
8+
9+
public class OrderedResultsGatherer implements IInvokedMethodListener {
10+
11+
List<Long> startTimes = new ArrayList<>();
12+
13+
public List<Long> getStartTimes() {
14+
return startTimes;
15+
}
16+
17+
@Override
18+
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
19+
startTimes.add(testResult.getStartMillis());
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.dependent.issue550;
2+
3+
import org.testng.annotations.BeforeMethod;
4+
import org.testng.annotations.Test;
5+
6+
public class TestClassSample {
7+
8+
@Test(dependsOnMethods = "a")
9+
public void b() {}
10+
11+
@BeforeMethod
12+
public void a() {}
13+
}

0 commit comments

Comments
 (0)