5

I know you can define in your xml which groups that you want to run, but I want to know whether it is possible to say run these methods if they are both member of groups A & B.

Let's say I have the following test cases;

@Test(groups={"A","B"})
public testA() {}

@Test(groups={"B","C"})
public testB(){}

and following configuration;

<test name="Test A|B">
<groups>
       <run>
           <include name="B" />
       </run>
    </groups>

    <classes>
        <class name="com.test.Test" />
    </classes>
</test>

This will run both testA and testB since they are both members of group B. I want to run the test only if it is member of both groups A & B.

Is it possible to do such thing with TestNG?

Thanks in advance

Jail
  • 880
  • 2
  • 15
  • 29

3 Answers3

5

You can create a listener implementing IMethodInterceptor interface. Which will give you an ability to access groups list from your @Test and manage your "tests to execute list" as you need. At same time ITestContext parameter allows you to access the data from testNg xml. So, you can set groups to run in default testNg manner (suite xml file); but run them depending on algorithm you implement. Something like:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;
import org.testng.annotations.Test;

public class Interceptor implements IMethodInterceptor
{

    @Override
    public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context)
    {
        int methCount = methods.size();
        List<IMethodInstance> result = new ArrayList<IMethodInstance>();

        for (int i = 0; i < methCount; i++)
        {
            IMethodInstance instns = methods.get(i);
            List<String> grps = Arrays.asList(instns.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).groups());
//get these groups from testng.xml via context method parameter                         
            if (grps.contains("A") && grps.contains("B"))
            {
                result.add(instns);
            }                       
        }                       
        return result;
    }
}
user1058106
  • 530
  • 3
  • 12
  • Good way! Another option can also be by implementing the iannotationtransformer. – niharika_neo May 16 '14 at 13:52
  • 1
    @niharika_neo As I understand IAnnotationTransformer will not allow an access to external data (e.g. testng.xml test context). So, in this particular case group names should be hard-coded, which IMHO is not a very good approach. Please, correct me if I'm wrong... – user1058106 May 16 '14 at 14:04
  • Thanks guys. @niharika_neo I'll take a look at that one as well. – Jail May 16 '14 at 14:05
  • That's correct and surely it shouldn't be hardcoded :) ..and apologies for not filling in the dots..this would also involve itestlistener to get the testcontext and set the groups – niharika_neo May 16 '14 at 14:19
  • On second thoughts, this does seem to be a better solution, since the solution would not be spread across multiple places. :) – niharika_neo May 16 '14 at 14:23
  • This is going to only work if you don't have @Test annotations at class level / it won't work (reliably) if you do define default groups on class level. – mac Jun 19 '15 at 17:30
4

You can do this with the TestNG beanshell support: http://testng.org/doc/documentation-main.html#beanshell (you are getting access to method, testngMethod, and groups)

In your particular case, it would be something like this:

<method-selectors>
  <method-selector>
    <script language="beanshell"><![CDATA[
       return groups.containsKey("A") && groups.containsKey("B");
     ]]></script>
  </method-selector>
</method-selectors>

You could also do a System.out.println() if you wanted to debug stuff.

The catch here is that you need to define the "method-selectors" in the "test" block. For some reason, if you define the "method-selectors" in "suite" (which is syntactically correct), the filter is not applied.

Debugging this with System.out.println() is going to help you understand whether the stuff even runs, or which methods it filters, so e.g.:

<method-selectors>
  <method-selector>
    <script language="beanshell"><![CDATA[
      runMethod = groups.containsKey("A") && groups.containsKey("B");
      System.out.println("run '" + testngMethod.getMethodName() + "'? -> " + runMethod);
      return runMethod;
    ]]></script>
  </method-selector>
</method-selectors>
mac
  • 2,672
  • 4
  • 31
  • 43
0

Did you try this?

<test name="Test A|B">
<groups>
       <run>
           <include name="A" />
            <include name="B" />
       </run>
    </groups>

    <classes>
        <class name="com.test.Test" />
    </classes>
</test>

If that doesn't work, you can "hack" around it by having 3 test methods in your class (with preserve order turned on) and have the first 2 tests set a flag on a condition, and then then 3rd test only runs if both conditions are set.

djangofan
  • 28,471
  • 61
  • 196
  • 289
  • 2
    I haven't tried that, but by definition it should run the first test twice and second test once. I'll give it a shot tomorrow though. Second option might have worked only if there were couple of tests. This is a huge project and we have hundreds of test cases. Thanks for throwing some ideas though. – Jail May 16 '14 at 01:41
  • you could use BeforeMethod annotated methods in a BaseClass that sets multiple flags that control what runs within your test classes. that way you can hide some of that logic? Just a thought. – djangofan May 16 '14 at 15:24