Vikas
Vikas

Reputation: 1101

how to get TestNG-@Factory to instantiate test classes in a particular order?

I am using TestNG for my selenium tests. There is a test called "Create Firm" which needs to be run multiple times on my laptop.

So I have written a class called "CreateFirm" for this, and the data for various firms reside in an excel spreadsheet.

At various times, I need to create various sets of firms, which I control using a column in Excel spreadsheet , which holds my computer name.

I use the @Factory to create my "CreateFirm" classes, which has one @Test method to create a Firm.

In excel spreadsheet If i have assigned Firm1,Firm2,Firm3,Firm4 in the same order to my laptop, @Factory creates them in a random order like Firm4,Firm3,Firm1,Firm2

My question is how to get @Factory to create test instances in the order that I want ?

My @Factory method is

      @Factory
  public Object[] runCreateFirm()
  {

        //This is where I get the list of test cases assigned to my laptop
        this.test_id_list=get_test_ids_for_test_run("Create Firm (class approach).xls", "Global");      

        Object[] result = new Object[this.test_id_list.size()];


        int index=0 ;
        for (String firm_id: this.test_id_list)
        {
            //This is where I get all the test data from the Excel spreadsheet
            HashMap<String,String> test_data_row=this.get_row_from_excel("Create Firm (class approach).xls", "Global", "test_case_id", firm_id);

            System.out.println("Inside Firm Factory ,index="+index +", test case id="+ test_data_row.get("test_case_id"));

            //CreateFirm is the class which will use the data and do all the UI actions to create a Firm
            result[index]=new CreateFirm(test_data_row);
            index++;
        }
        return result;
  }

XML is

    <?xml version="1.0" encoding="UTF-8"?>
<suite name="CreateFirm Suite">
  <test name="Create Firm Test"  order-by-instances="false">
    <classes>
      <class name="regressionTests.factory.CreateFirmFactory"/>
    </classes>
  </test>
</suite>

Upvotes: 0

Views: 3109

Answers (3)

Rama Krishna Hati
Rama Krishna Hati

Reputation: 41

Referred https://github.com/cbeust/testng/issues/1410 for the solution and got a hint from example given by krmahadevan. Thanks a bunch KR. I am using TestNG plugin version 6.14.0.201802161500 in Eclipse.

Just overriding toString method in the Test Class does the trick. In this toString method return the key parameter e.g. Firm1, Firm2 etc. Please refer following code for a clear understanding.

TestClass.java

import org.testng.annotations.Test;

public class TestClass {

    String testcaseName;

    public TestClass(String testcaseName) {
        this.testcaseName = testcaseName;
    }

    @Test
    public void sendCmd() {
        System.out.println("Testcase received "+this.testcaseName);
    }

    @Override 
    public String toString() { 
        return this.testcaseName; 
    }
}

FactoryClass.java

import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;

public class FactoryClass {

    @DataProvider
    public static String[][] createTestCmds() {
       String[][] testData = new String[6][1];
        for (int i = 0, j= 6; i < 6 ; i++,j--) {
            testData[i][0]="Firm "+i;
        }
       return testData;
    }

    @Factory(dataProvider = "createTestCmds")
    public Object[] Factory(String testcaseName) {
        return new Object[] {new TestClass(testcaseName)};
    }
}

Test.java

import java.util.Collections;

import org.testng.TestNG;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlTest;

public class Test {
    public static void main(String[] args) {
        TestNG testng = new TestNG();
        XmlSuite xmlSuite = new XmlSuite();
        xmlSuite.setGroupByInstances(true);
        xmlSuite.setName("Sample_Test_Suite");
        XmlTest xmlTest = new XmlTest(xmlSuite);
        xmlTest.setName("Sample_Test");
        xmlTest.setClasses(Collections.singletonList(new XmlClass(FactoryClass.class)));
        testng.setXmlSuites(Collections.singletonList(xmlSuite));
        testng.setVerbose(2);
        System.err.println("Printing the suite xml file that would be used.");
        System.err.println(xmlSuite.toXml());
        testng.run();
    }
}

Result

Testcase received Firm 0
Testcase received Firm 1
Testcase received Firm 2
Testcase received Firm 3
Testcase received Firm 4
Testcase received Firm 5
PASSED: sendCmd on Firm 0
PASSED: sendCmd on Firm 1
PASSED: sendCmd on Firm 2
PASSED: sendCmd on Firm 3
PASSED: sendCmd on Firm 4
PASSED: sendCmd on Firm 5

===============================================
    Sample_Test
    Tests run: 6, Failures: 0, Skips: 0
===============================================


===============================================
Sample_Test_Suite
Total tests run: 6, Failures: 0, Skips: 0
===============================================

This problem I faced. I got a solution as mentioned above.

Upvotes: 0

Vikas
Vikas

Reputation: 1101

Managed to write the interceptor method for my requirement. To start with I introduced a new field in my class called "priority" . I couldnt use @Priority because, my test class had only one test method, in which case all my instantiations will have the same priority. So I introduced a new field called priority which will have the order in which which the class should be instantiated. I use that field in the interceptor method to set the order of instantiation

public List<IMethodInstance> intercept(List<IMethodInstance> methods,ITestContext context) 
{

    List<IMethodInstance> result = new ArrayList<IMethodInstance>();
    int array_index=0;

    for (IMethodInstance m : methods)
    {
        result.add(m);
    }
    //Now iterate through each of these test methods
    for (IMethodInstance m : methods)
    {
        try {               
            //Get the FIELD object from - Methodobj->method->class->field
            Field f = m.getMethod().getRealClass().getField("priority");
            //Get the object instance of the method object              
            array_index=f.getInt(m.getInstance());
        } 
         catch (Exception e) {
            e.printStackTrace();
        }           
        result.set(array_index-1, m);           
    }

    return result;
}

Upvotes: 2

Cedric Beust
Cedric Beust

Reputation: 15608

The factory instantiates test classes, it doesn't run them. If you need a specific order for your tests, you have a lot of choices between dependencies (groups and methods), priorities and method interceptors.

Upvotes: 1

Related Questions