Custom test method name in TestNG reports

后端 未结 6 1880
执念已碎
执念已碎 2020-12-05 05:10

I am working on a project where I need to invoke TestNG programatically(using data providers). Things are fine except that in the report, we are getting the name of the @Tes

6条回答
  •  无人及你
    2020-12-05 05:25

    The answer by artdanil did not completely solve my problem, the test name is not updated in emailable report.

    Answer posted by @jersey-city-ninja does update the name in Emailable report but it repeats the same-last updated testname for all the Dataprovider values because what pilotg2 posted is true for tests that consume Dataprovider i.e. the getTestName method keeps returning the last set name for a method and all the testnames for a dataprovider are same.

    So here is the answer that is combination of answer posted by @pilotg2 and @jersey-city-ninja and with additional step to overcome duplicate method names.

    Note this updates the testname in Emailable report, XML report, HTML report, Junit report. I don't see it updating the Eclipse - TestNg execution view - will update if I found something

    import org.testng.Assert;
    import org.testng.ITest;
    import org.testng.ITestContext;
    import org.testng.ITestResult;
    import org.testng.annotations.AfterMethod;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.BeforeMethod;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Test;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Iterator;
    import java.util.List;
    
    
    public class NewDataProviderTest implements ITest {
        //The Java ThreadLocal class enables you to create variables that can only be read and written by the same thread
        private ThreadLocal testName = new ThreadLocal<>();
    
        /*
        * TestNG, for some reason, when building different reports, calls getName() on the test while building the report.
        * This is fine if you are not using a data provider to generate different runs and set a unique name for each run by using the ITest strategy.
        * If you are using a data provider to generate multiple runs of the same test and want each run to have a unique name then there is a problem.
        * As the ITest strategy returns the name for the test as the name set by the last run.
        * */
        private int emailNameIndex = 0;
        private int htmlNameIndex = 0;
        private int xmlNameIndex = 0;
        private ArrayList allTests = new ArrayList();
        /*
        * TestHTMLReporter gets the name by first getting all the names for failed tests and then the names for passing tests
        * Hence keeping them in 2 separate lists
        * */
        private ArrayList passedTests = new ArrayList();
        private ArrayList failedTests = new ArrayList();
    
        @BeforeClass(alwaysRun = true)
        public void initialize() {
            this.testName.set("");
        }
    
        @BeforeMethod(alwaysRun = true)
        public void setCustomTestcaseName(Method method, Object[] testData) {
            //Set the default name
            this.testName.set(method.getName());
            //Change the test name only if Dataprovider is used
            //Check if data provider is used in the test
            if (testData != null && testData.length > 0) {
                System.out.println("\n\nParameters "+testData[0]+" are passed to the test - "+method.getName());
                //Taking 1st field in the Dataprovider for updating testname - can be changed as desired maybe using a variable
                //I'm changing the name only if the Dataprovider field is String
                if (testData[0] instanceof String) {
                    //Taking 1st field in the Dataprovider for updating testname - can be changed as desired
                    System.out.println("I'm setting custom name to the test as "+method.getName() + "_" + testData[0]);
                    this.testName.set(method.getName() + "_" + testData[0]);
                }
    
            }
            //Add the name to the collection that stores all list names
            allTests.add(testName.get());
    
        }
    
         @AfterMethod (alwaysRun = true)
         public void setTheTestcaseNameInResult(ITestResult result, Method method) {
            //Fill the Passed and Failed tests collections
             try {
                 if(result.getStatus() == ITestResult.SUCCESS) {
                     System.out.println("Adding "+ result.getTestName() + " to passed tests collection");
                     passedTests.add(result.getTestName());
                 }
                 if(result.getStatus() == ITestResult.FAILURE) {
                     System.out.println("Adding " + result.getTestName() + " to FAILURE tests collection");
                     failedTests.add(result.getTestName());
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             }
             // To change display name in HTML report
             //Only changing the name if the parameter is instance of String
             if(iTestResult.getParameters().length > 0) {
                 if (iTestResult.getParameters()[0] instanceof String) {
                     System.out.println("Updating the name as Parameters are passed to the test-"+method.getName());
                     try {
                         /* This helps in setting unique name to method for each test instance for a data provider*/
                         Field resultMethod = TestResult.class.getDeclaredField("m_method");
                         resultMethod.setAccessible(true);
                         resultMethod.set(iTestResult, iTestResult.getMethod().clone());
    
                         Field methodName = org.testng.internal.BaseTestMethod.class.getDeclaredField("m_methodName");
                         methodName.setAccessible(true);
                         methodName.set(iTestResult.getMethod(), this.getTestName());
                     } catch (Exception e) {
                         e.printStackTrace();
                     }
                     System.out.println("New Name is - " + iTestResult.getMethod().getMethodName());
    
                 }
             }
         }
    
        @Override
        public String getTestName() {
            String name = testName.get();
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();// .toString();
            //This is called
            if (isCalledFromMethod(stackTrace, "XMLSuiteResultWriter")) {
                //System.out.println("Got called from XMLSuiteResultWriter");
                if (allTestNames.size() > 0) {
                    if (xmlNameIndex < allTestNames.size()) {
                        name = allTestNames.get(xmlNameIndex);
                    } else {
                        name = allTestNames.get(0);
                    }
                } else {
                    name = "undefined";
                }
                xmlNameIndex++;
                if (xmlNameIndex >= allTestNames.size()) {
                    xmlNameIndex = 0;
                }
                // System.out.println("Got called from XMLSuiteResultWriter returning name - "+name);
            } else if (isCalledFromMethod(stackTrace, "EmailableReporter")) {
                if (allTestNames.size() > 0) {
                    if (emailNameIndex < allTestNames.size()) {
                        name = allTestNames.get(emailNameIndex);
                    } else {
                        name = allTestNames.get(0);
                    }
                } else {
                    name = "undefined";
                }
                emailNameIndex++;
                if (emailNameIndex >= allTestNames.size()) {
                    emailNameIndex = 0;
                }
                System.out.println("Got called from EmailableReporter returning name -"+name);
            }
            if (isCalledFromMethod(stackTrace, "TestHTMLReporter")) {
                if (allTestNames.size() <= 0) {
                    name = "undefined";
                } else {
                    if (htmlNameIndex < failedTestNames.size()) {
                        name = failedTestNames.get(htmlNameIndex);
                    } else {
                        int htmlPassedIndex = htmlNameIndex - failedTestNames.size();
                        if (htmlPassedIndex < passedTestNames.size()) {
                            name = passedTestNames.get(htmlPassedIndex);
                        } else {
                            name = "undefined";
                        }
                    }
                }
                htmlNameIndex++;
                if (htmlNameIndex >= allTestNames.size()) {
                    htmlNameIndex = 0;
                }
                System.out.println("Got called from TestHTMLReporter returning name - "+name);
            }
            System.out.println("Returning testname as-"+name);
            return name;
        }
    
        private boolean isCalledFromMethod(StackTraceElement[] stackTrace, String checkForMethod) {
            boolean calledFrom = false;
            for (StackTraceElement element : stackTrace) {
                String stack = element.toString();
                // System.out.println("Rohit the called from value is:"+stack);
                if (stack.contains(checkForMethod))
                    calledFrom = true;
            }
            return calledFrom;
        }
    
    
      @Test(groups= {"L1", "L2", "L3"}, dataProvider = "dp1")
      public void dataProviderTest(String username) {
          System.out.println("\n\nI'm in dataProviderTest with data-"+username);
          /* Fail the test if value is L2 - deliberately so that we have failed test in report */ 
          if(username.contains("L2")) {
              Assert.fail();
          }
    
      }
    
      @Test(dependsOnMethods = "dataProviderTest", groups= {"L1", "L2", "L3"}, dataProvider = "dp1")
      public void dataProviderDependentTest(String username) {
          System.out.println("\n\nI'm in dataProvider DEPENDENT Test with data-"+username);
    
      }
    
      //This test consumes data of type list so the name will not be updated in report
      @Test(groups= {"L1", "L2", "L3"}, dataProvider = "dp2")
      public void dataListProviderTest(List list) {
          Object[] arr = list.get(0);
            List arrList = Arrays.asList(arr);
            Iterator iterator = arrList.iterator();
            while (iterator.hasNext()) {
    
            String[] data = (String[]) iterator.next();
            System.out.println("In list test - "+data[0]);
            }    
    
      }
    
      @DataProvider(name="dp1")
      public Object[][] getDataForTest(ITestContext iTestContext){
          Object[][] L1 = new Object[][] {
              {"L1"}, {"L2"}, {"L3"}
          };
    
    
          return L1;
      }
    
    
      @DataProvider(name="dp2")
      public Object[][] getDataListForTest(ITestContext iTestContext){
          List list = new ArrayList();
          Object[][] L1 = new Object[][] {
              new String [] {"L1", "l1"}, 
              new String [] {"L1", "l1"}
          };
    
         list.add(L1);
          return new Object[][] { { list } };
      }
    
    }
    
        

    提交回复
    热议问题