TestNG报告中自定义测试方法名称

24
我正在处理一个项目,需要通过数据提供者编程方式调用TestNG。 一切都很好,除了在报告中,我们得到的是一个泛型名称来处理许多情况下的@Test方法名。 我们希望在报告中获得有意义的名称。
我做了研究,找到了三种方法,但不幸的是,所有这些方法都对我失败了。
1) 实现ITest
我在这里这里找到了相关信息。
我设置了我想要的名称,只要我进入@Test方法(对于我尝试的所有3种方法,这就是我设置名称的方式)。 这个名称从getTestName()返回。 我观察到getTestName()在我的@Test之前和之后都被调用。 最初,它返回null(为了处理NullPointerException,我返回""而不是null),然后它返回正确的值。 但是我没有看到这在报告中反映出来。

编辑: 也尝试按照artdanil的建议从@BeforeMethod设置名称

2和3

两者都基于上述第二个链接中给出的解决方案

通过在XmlSuite中重写setName,我得到了

Exception in thread "main" java.lang.AssertionError: l should not be null
        at org.testng.ClassMethodMap.removeAndCheckIfLast(ClassMethodMap.java:58)
        at org.testng.internal.TestMethodWorker.invokeAfterClassMethods(TestMethodWorker.java:208)
        at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:114)
        at org.testng.TestRunner.privateRun(TestRunner.java:767)
        ...

通过重写toString()方法,我在日志中看到了这些内容(带有我的注释),但是报告中没有更新。
[2013-03-05 14:53:22,174] (Main.java:30) - calling execute 
    [2013-03-05 14:53:22,346] GenericFunctionTest.<init>(GenericFunctionTest.java:52) - inside constructor
    [2013-03-05 14:53:22,372] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning **//this followed by 3 invocations before arriving at @Test method**
    [2013-03-05 14:53:22,410] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning 
    [2013-03-05 14:53:22,416] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning 
    [2013-03-05 14:53:22,455] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning 
    [2013-03-05 14:53:22,892] GenericFunctionTest.<init>(GenericFunctionTest.java:52) - inside constructor 
    [2013-03-05 14:53:23,178] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning **//again blank as i havent set it yet**
    [2013-03-05 14:53:23,182] GenericFunctionTest.getResult(GenericFunctionTest.java:69) - inside with test case:TestCase{signature=Signature{...}}**//I am setting it immedietely after this**
    [2013-03-05 14:53:23,293] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning MyMethodName **//What i want**
    [2013-03-05 14:53:23,299] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning MyMethodName **// again**

编辑:再次尝试通过硬编码一个值来设置所有3个测试方法的输入,而不是在测试方法进入时设置。但结果相同。


我通过使用TestNG报告监听器来触发一个类,该类根据ISuite结果构建我的HTML来实现这一点。要在测试报告中显示任意数据,例如测试参数值,您需要为每个测试向ITestContext添加数据,以便报告编写者可以访问附加数据。幸运的是,方法名已经是上下文的一部分,你可以检索它。 - djangofan
7个回答

16

我曾经有同样的问题,发现在使用带有@BeforeMethod注释的方法中设置存储测试用例名称的字段,并使用TestNG的本地依赖注入提供方法名和测试参数可以帮助解决这个问题。测试名称取自由DataProvider提供的测试参数。如果您的测试方法没有参数,请报告该方法的名称。

//oversimplified for demontration purposes
public class TestParameters {
    private String testName = null;
    private String testDescription = null;

    public TestParameters(String name,
                          String description) {
        this.testName = name;
        this.testDescription = description;
    }

    public String getTestName() {
        return testName;
    }
    public String getTestDescription() {
        return testDescription;
    }
}

public class SampleTest implements ITest {
    // Has to be set to prevent NullPointerException from reporters
    protected String mTestCaseName = "";

    @DataProvider(name="BasicDataProvider")
    public Object[][] getTestData() {
        Object[][] data = new Object[][] {
                { new TestParameters("TestCase1", "Sample test 1")},
                { new TestParameters("TestCase2", "Sample test 2")},
                { new TestParameters("TestCase3", "Sample test 3")},
                { new TestParameters("TestCase4", "Sample test 4")},
                { new TestParameters("TestCase5", "Sample test 5") }
        };
        return data;
    }

    @BeforeMethod(alwaysRun = true)
    public void testData(Method method, Object[] testData) {
        String testCase = "";
        if (testData != null && testData.length > 0) {
            TestParameters testParams = null;
            //Check if test method has actually received required parameters
            for (Object testParameter : testData) {
                if (testParameter instanceof TestParameters) {
                    testParams = (TestParameters)testParameter;
                    break;
                }
            }
            if (testParams != null) {
                testCase = testParams.getTestName();
            }
        }
        this.mTestCaseName = String.format("%s(%s)", method.getName(), testCase);
    }

    @Override
    public String getTestName() {
        return this.mTestCaseName;
    }

    @Test(dataProvider="BasicDataProvider")
    public void testSample1(TestParameters testParams){
        //test code here
    }

    @Test(dataProvider="BasicDataProvider")
    public void testSample2(TestParameters testParams){
        //test code here
    }

    @Test
    public void testSample3(){
        //test code here
    }
}

编辑: 根据下面的评论,我意识到从报告中提取一个样本将会很有用。

上述代码运行后生成的报告摘录如下:

<testng-results skipped="0" failed="0" total="5" passed="5">
  <suite name="SampleTests" duration-ms="2818" started-at="<some-time>" finished-at="<some-time>">
    <test name="Test1" duration-ms="2818" started-at="<some-time>" finished-at="<some-time>">
        <test-method 
            status="PASS" 
            signature="testSample1(org.example.test.TestParameters)[pri:0, instance:org.example.test.TimeTest@c9d92c]"
            test-instance-name="testSample1(TestCase5)"
            name="testSample1" 
            duration-ms="1014"
            started-at="<some-time-before>" 
            data-provider="BasicDataProvider" 
            finished-at="<some-time-later>" >
            <!-- excluded for demonstration purposes -->
        </test-method>
        <!-- the rest of test results excluded for brevity -->
    </test>
  </suite>
</testng-result>
请注意,从getTestName()方法返回的值在test-instance-name属性中,而不是name属性中。

1
我正在检查HTML报告。是的,在XML中,test-instance-name显示正确。但用户们正在检查HTML报告。不知道这个能否在那里反映出来? - rajesh
1
错过了你的编辑。所以我认为我错误地假设它将是测试名称,而不是测试实例名称。授予您赏金,因为它提高了我的学习成果。谢谢! - rajesh
注意:ITest.setTestName方法实际上设置了一个在Reporter.log输出的开头显示为“Instance Name:” 的值。当我发现这一点时,感到非常不直观。 - djangofan
我尝试实现相同的功能,但在报告中,test-instance-name 总是显示我运行的测试的最后一个名称。即使在您的输出中,它显示“testSample1(TestCase5)”,但不包括之前的测试名称。 - ModdyFire
"testData"方法中传递的"Method"参数是什么?我们是如何解决这个问题的? - gonephishing
显示剩余3条评论

4

我遇到了类似的问题。

首先,我实现了已经提到的ITest策略。这是解决方案的一部分,但并不完全。

TestNG在构建不同的报告时,以某种原因调用测试的getName()函数来构建报告。如果您没有使用数据提供程序生成不同的运行,并通过使用ITest策略为每个运行设置唯一名称,则这是可以接受的。如果您正在使用数据提供程序生成同一测试的多个运行,并希望每个运行都具有唯一名称,则存在问题。因为ITest策略将测试名称保留为最后一个运行设置的名称。

所以我不得不实现一个非常自定义的getName()函数。在我的特定情况下,有一些假设:

  1. 只运行三个报告:TestHTMLReporter、EmailableReporter和XMLSuiteResultWriter。
  2. 只要getName()未作为任何假定报告者之一的结果被调用,那么返回当前设置的名称就可以了。
  3. 当报告者运行时,它按顺序进行getName()调用,并且每次运行只调用1次。
public String getTestName()
{
    String name = testName;
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();//.toString();
    if(calledFrom(stackTrace, "XMLSuiteResultWriter"))
    {
        name = testNames.size()>0?testNames.get(xmlNameIndex<testNames.size()?xmlNameIndex:0):"undefined";
        xmlNameIndex++;
        if(xmlNameIndex>=testNames.size())
            xmlNameIndex = 0;
    }
    else if(calledFrom(stackTrace, "EmailableReporter"))
    {
        name = testNames.size()>0?testNames.get(emailNameIndex<testNames.size()?emailNameIndex:0):"undefined";
        emailNameIndex++;
        if(emailNameIndex>=testNames.size())
            emailNameIndex = 0;
    }
    if(calledFrom(stackTrace, "TestHTMLReporter"))
    {
        if(testNames.size()<0)
        {
            name = "undefined";
        }
        else
        {
            if(htmlNameIndex < testNamesFailed.size())
            {
                name = testNamesFailed.get(htmlNameIndex);
            }
            else
            {
                int htmlPassedIndex = htmlNameIndex - testNamesFailed.size();
                if(htmlPassedIndex < testNamesPassed.size())
                {
                    name = testNamesPassed.get(htmlPassedIndex);
                }
                else
                {
                    name = "undefined";
                }
            }
        }
        htmlNameIndex++;
        if(htmlNameIndex>=testNames.size())
            htmlNameIndex = 0;
    }
    return name;
}

private boolean calledFrom(StackTraceElement[] stackTrace, String checkForMethod)
{
    boolean calledFrom = false;
    for(StackTraceElement element : stackTrace)
    {
        String stack = element.toString();
        if(stack.contains(checkForMethod))
            calledFrom = true;
    }
    return calledFrom;
}

当设置运行名称时(我是在我定义的@BeforeMethod(alwaysRun=true)方法中按照ITest策略添加名称的),我将名称添加到一个ArrayList testNames中。但是,HTML报告不正确。大多数其他报告以顺序提取信息,例如XMLSuiteResultWriter,但是TestHTMLReporter先获取所有失败测试的名称,然后获取通过测试的名称。因此,我不得不实现两个额外的ArrayList:testNamesFailed和testNamesPassed,并根据测试是否通过将测试名称添加到它们中。

我自由地承认这非常粗糙且非常脆弱。理想情况下,TestNG在运行时将测试添加到集合中,并从该集合中获取名称,而不是从原始测试中获取名称。如果您让TestNG运行其他报告,您将不得不找出它们请求名称的顺序以及在线程堆栈跟踪中搜索的足够唯一的字符串是什么。

--编辑1

或者,使用ITest Strategy和工厂模式(@factory注释)。

TestNG Using @Factory and @DataProvider

http://beust.com/weblog/2004/09/27/testngs-factory/

这确实需要一些小的更改。这包括创建一个与原始测试方法具有相同参数的构造函数。现在,测试方法没有参数。您可以在新构造函数中设置名称,然后在getTestName方法中简单地返回该名称。确保从测试方法中删除数据提供程序规范。


3
请查看以下代码,用于在TestNG报告中设置测试用例的自定义名称。
这段代码具有以下功能。
  • Dynamic execution on same test-case in multiple time
  • Set custom test-case name for reports
  • Set parallel execution of multiple test-cases execution

    import java.lang.reflect.Field;
    import org.testng.ITest;
    import org.testng.ITestResult;
    import org.testng.Reporter;
    import org.testng.annotations.AfterMethod;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Factory;
    import org.testng.annotations.Test;
    import org.testng.internal.BaseTestMethod;
    import com.test.data.ServiceProcessData;
    
    public class ServiceTest implements ITest {
    
    protected ServiceProcessData serviceProcessData;
    protected String testCaseName = "";
    
    @Test
    public void executeServiceTest() {
        System.out.println(this.serviceProcessData.toString());
    }
    
    @Factory(dataProvider = "processDataList")
    public RiskServiceTest(ServiceProcessData serviceProcessData) {
        this.serviceProcessData = serviceProcessData;
    }
    
    @DataProvider(name = "processDataList", parallel = true)
    public static Object[] getProcessDataList() {
    
        Object[] serviceProcessDataList = new Object[0];
        //Set data in serviceProcessDataList
        return serviceProcessDataList;
    }
    
    @Override
    public String getTestName() {
    
        this.testCaseName = "User custom testcase name";
    
        return this.testCaseName;
    }
    
    @AfterMethod(alwaysRun = true)
    public void setResultTestName(ITestResult result) {
        try {
            BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod();
            Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName");
            f.setAccessible(true);
            f.set(baseTestMethod, this.testCaseName);
        } catch (Exception e) {
            ErrorMessageHelper.getInstance().setErrorMessage(e);
            Reporter.log("Exception : " + e.getMessage());
        }
    }}
    

    Thanks


当您使用Kotlin编写时,这是否有效?我遇到了这个问题:无法将org.testng.internal.BaseTestMethod.m_methodName的最终java.lang.String字段设置为java.lang.ThreadLocal - kotoMJ
这个@AfterMethod技巧是我唯一可行的选择。不幸的是,它似乎只适用于HTML报告,并且在Eclipse中TestNG插件创建的视图中 起作用。 - lmsurprenant

3

如果你想要在HTML报告中修改名称,那么这将是一个非常麻烦的过程。以下是我实现该功能的方法:

public class NinjaTest {
...
...
@AfterMethod (alwaysRun = true)
public void afterMethod(ITestResult result, Method method) {
    try {
        //I have XML test suites organized in directories. 
        String xmlFile = result.getTestContext().getCurrentXmlTest().getSuite().getFileName();
        String suiteName = xmlFile.substring(xmlFile.lastIndexOf("\\") + 1, xmlFile.lastIndexOf(".xml"));
        String pathToFile = xmlFile.substring(0, xmlFile.lastIndexOf("\\") );
        String directory = pathToFile.substring(pathToFile.lastIndexOf("\\") + 1);
        String testMethodName = String.format("%s/%s - %s", directory, suiteName, method.getName());

        //Total hack to change display name in HTML report  \(^o^)/ 
        Field methodName = org.testng.internal.BaseTestMethod.class.getDeclaredField("m_methodName");
        methodName.setAccessible(true);
        methodName.set(result.getMethod(), testMethodName);
    } catch (Exception e) {
        // Eh....  ¯\_(ツ)_/¯
        e.printStackTrace();
    }
}
...
...

2

artdanil的回答没有完全解决我的问题,在emailable报告中测试名称没有更新。

@jersey-city-ninja发布的答案会更新Emailable报告中的名称,但它会为所有Dataprovider值重复相同的最后更新的测试名称,因为pilotg2发布的内容对于使用Dataprovider的测试是正确的,即getTestName方法保持返回方法的最后设置名称,并且Dataprovider的所有测试名称都相同。

所以这里是答案,它是@pilotg2和@jersey-city-ninja发布的答案的组合,并带有额外的步骤来克服重复的方法名称。

注意:这将更新Emailable报告、XML报告、HTML报告和Junit报告中的测试名称。我没有看到它更新Eclipse-TestNg执行视图-如果我找到了什么,我会更新。

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<String> 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<String> allTests = new ArrayList<String>();
    /*
    * 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<String> passedTests = new ArrayList<String>();
    private ArrayList<String> failedTests = new ArrayList<String>();

    @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<String[]> list) {
      Object[] arr = list.get(0);
        List<Object> arrList = Arrays.asList(arr);
        Iterator<Object> 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<Object[][]> list = new ArrayList<Object[][]>();
      Object[][] L1 = new Object[][] {
          new String [] {"L1", "l1"}, 
          new String [] {"L1", "l1"}
      };

     list.add(L1);
      return new Object[][] { { list } };
  }

}

1
尝试实现org.testng.ITest接口,该接口需要一个getTestName()方法。这样报告会正确处理返回的值。

@Rajesh,你解决了吗?如果是,请说明你是如何解决的,因为这将帮助很多人,包括我解决面临的问题。我现在正在使用最新的TestNG版本7.0-beta设置测试用例名称时遇到了完全相同的问题。 - sjethvani

1

遇到了相同的问题,这是我们解决它的方法:

问题在于所有的ITestResult结果对象共享一个ITestNGMethod对象实例,因此当方法名改变时会影响所有的结果。

来自:org.testng.reporters.XMLSuiteResultWriter#getTestResultAttributes

    attributes.setProperty(XMLReporterConfig.ATTR_NAME, testResult.getMethod().getMethodName());

name属性是从testResult.getMethod().getMethodName()中读取的。

基本上,我们为每个结果克隆了方法对象,因此每个结果都可以有独立的getMethod()。

我们添加了以下代码:

  @AfterMethod(alwaysRun = true)
public void setResultTestName(ITestResult result, ITestContext context) {
    try {
        BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod().clone();

        String featureName = StringUtils.substringBetween(result.getParameters()[1].toString(), "[", "]");
        String scenarioName = result.getParameters()[0].toString().replace("\"", "");
        Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName");
        f.setAccessible(true);
        f.set(baseTestMethod, featureName + " - " + scenarioName);

        f = result.getClass().getDeclaredField("m_method");
        f.setAccessible(true);
        f.set(result, baseTestMethod);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接