Reputation: 273
I have a class with several tests split into two groups. I want a strict ordering to when the grouped tests are run, such that the tests in group A run first, then a setup method for group B is executed, and then group B runs. For example:
@Test(groups="A")
public void a1() {
// ...
}
@Test(groups="A")
public void a2() {
// ...
}
@BeforeGroups(value="B", dependsOnGroups="A")
public void setupB() {
// ...
}
@Test(groups="B")
public void b1() {
// ...
}
@Test(groups="B")
public void b2() {
// ...
}
The problem I'm running into is that TestNG doesn't seem to be honoring the setupB method. Instead of the expected execution order:
a1/a2
a2/a1
setupB
b1/b2
b2/b1
It executes something like this:
a1
setupB
b1
a2
b2
Any idea what I'm doing wrong with this setup? Am I missing something conceptually about how TestNG's groups work?
Upvotes: 4
Views: 407
Reputation: 677
As mentioned earlier, when a test method is invoked, it just checks whether the corresponding @BeforeGroups
annotated method has been executed for the test group the method belongs to, and if it hasn't, TestNG just invokes the @BeforeGroups
method.
The workaround is to add a custom IMethodInterceptor
which would add dependencies on the groups from @BeforeGroups#dependsOnGroups
for the test methods, related to the same group:
public class TestMethodInterceptor implements IMethodInterceptor {
@Override
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
if (!(context instanceof TestRunner)) {
return methods;
}
TestRunner testRunner = (TestRunner) context;
Collection<ITestClass> testClasses = testRunner.getTestClasses();
Map<String, List<String>> groupDependencies = MethodGroupsHelper.findGroupsMethods(testClasses, true).entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
groupBeforeMethods -> groupBeforeMethods.getValue().stream()
.flatMap(m -> Arrays.stream(m.getGroupsDependedUpon()))
.collect(Collectors.toList())
));
return methods.stream()
.map(IMethodInstance::getMethod)
.map(method -> {
Set<String> methodGroupDependencies = Stream.ofNullable(method.getGroups())
.flatMap(Arrays::stream)
.flatMap(group -> groupDependencies.getOrDefault(group, List.of()).stream())
.collect(Collectors.toSet());
return methodGroupDependencies.isEmpty() ? method : addGroupDependencies(method, methodGroupDependencies);
})
.map(MethodInstance::new)
.collect(Collectors.toList());
}
private ITestNGMethod addGroupDependencies(ITestNGMethod method, Collection<String> groups) {
String[] methodGroupDependencies;
if (method.getGroupsDependedUpon() == null || method.getGroupsDependedUpon().length == 0) {
methodGroupDependencies = groups.toArray(new String[0]);
} else {
methodGroupDependencies = Stream.concat(Arrays.stream(method.getGroupsDependedUpon()), groups.stream())
.distinct()
.toArray(String[]::new);
}
return new WrappedTestNGMethod(method) {
@Override
public String[] getGroupsDependedUpon() {
return methodGroupDependencies;
}
};
}
}
The TestMethodInterceptor
is a listener that can be added to the execution by means of @Listeners
annotation.
Upvotes: 0
Reputation: 1609
This might be a workaround for now if it works. Not sure if you can use both annotations together.
@Test(groups="A")
public void a1() {
// ...
}
@Test(groups="A")
public void a2() {
// ...
}
@BeforeGroups(value="B")
@AfterGroups(value="A")
public void setupB() {
// ...
}
@Test(groups="B")
public void b1() {
// ...
}
@Test(groups="B")
public void b2() {
// ..
. }
Upvotes: 0
Reputation: 3257
Try to specify dependsOnGroups
for test methods as well.
public class TestClass {
@Test(groups="B")
public void b1() {
System.out.println("b1");
}
@Test(groups="B")
public void b2() {
System.out.println("b2");
}
@Test(groups="A", dependsOnGroups="B")
public void a1() {
System.out.println("a1");
}
@Test(groups="A", dependsOnGroups="B")
public void a2() {
System.out.println("a2");
}
@BeforeGroups(value="A", dependsOnGroups="B")
public void setupA() {
System.out.println("before");
}
}
I may be wrong about it, but seems that if a test method that belongs to a group has been picked for execution and it does not depend on any groups or methods, it just causes @BeforeGroups
-annotated method to be run (ignoring dependsOnGroups
specified there). Note that TestNG does not guarantee the execution order without some explicit declaration, e.g. using "depends" or "priority" mechanisms.
Hopefully, Cedric Beust will pay this question a visit.
Upvotes: 2