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

我正在开发一个项目,我需要以编程方式调用TestNG(使用数据提供程序)。 一切都很好,除了在报告中,我们得到@Test方法的名称,这是一个处理许多情况的通用方法。 我们希望在报告中获得一个有意义的名称。

我正在研究这个并发现了3种方法,但不幸的是,所有方法都失败了。

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.(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.(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。 但结果相同

我遇到了同样的问题, @BeforeMethod使用@BeforeMethod注释的方法中设置存储测试用例名称的字段有@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 } } 

编辑:基于以下评论,我意识到报告中的样本将非常有用。

从上面运行代码的报告中提取:

           

请注意, getTestName()方法返回的值位于test-instance-name属性中,而不是name属性中。

如果您想在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(); } } ... ... 

我遇到了类似的问题。 首先,我实施了已提到的ITest策略。 这是解决方案的一部分,但并非完全如此。

TestNG出于某种原因,在构建不同的报告时,在构建报告时在测试中调用getName()。 如果您没有使用数据提供程序生成不同的运行并使用ITest策略为每次运行设置唯一名称,那么这很好。 如果您使用数据提供程序生成同一测试的多次运行,并希望每次运行具有唯一名称,那么就会出现问题。 由于ITest策略将测试的名称保留为上次运行设置的名称。

所以我必须实现一个非常自定义的getName()。 SOme假设(在我的特定情况下):

  1. 只运行三个报告:TestHTMLReporter,EmailableReporter,XMLSuiteResultWriter。
  2. 如果由于某个假定的记者没有调用名称,那么返回当前设置的名称就可以了。
  3. 当记者正在运行时,它会按顺序调用getName()调用,每次运行只调用一次。
 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; } else if(calledFrom(stackTrace, "EmailableReporter")) { name = testNames.size()>0?testNames.get(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; } 

在设置运行名称时(我在我根据ITest策略定义的@BeforeMethod(alwaysRun = true)方法中执行此操作)我将名称添加到ArrayList testNames。 但那时html报告不正确。 大多数其他报告按顺序提取信息,如XMLSuiteResultWriter,但TestHTMLReporter首先获取失败测试的所有名称,然后获取传递测试的名称。 所以我必须实现其他ArrayLists:testNamesFailed和testNamesPassed,并在测试完成时根据它们是否通过来为它们添加测试名称。

我会坦率地承认,这非常非常脆弱。 理想情况下,TestNG在运行时将测试添加到集合中,并从该集合中获取名称,而不是从原始测试中获取名称。 如果你有TestNG来运行其他报告,你将不得不弄清楚他们请求名称的顺序以及在线程堆栈跟踪中搜索的唯一足够的字符串。

– 编辑1

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

TestNG使用@Factory和@DataProvider

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

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

请在TestNG报告中找到以下代码来设置测试用例的自定义名称。

此代码中提供了以下function。

  • 多次在同一测试用例上动态执行
  • 为报告设置自定义测试用例名称
  • 设置多个测试用例执行的并行执行

     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()); } }} 

    谢谢

尝试实现需要getTestName()方法的org.testng.ITest接口。 这种方式报告正确处理返回的值。