当前位置: 首页 > 知识库问答 >
问题:

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

翟冷勋
2023-03-14

我正在从事一个需要以编程方式调用TestNG(使用数据提供程序)的项目。情况很好,只是在报告中,我们得到了@测试方法的名称,这是一种处理许多情况的通用方法。我们希望在报告中得到一个有意义的名字。

我一直在研究这个问题,发现了三种方法,但不幸的是,所有方法都失败了。

1) 实施ITest

我在这里和这里都发现了这个

我一输入@Test方法就设置了我想要的名称(对于我尝试的所有3种方式,这就是我设置名称的方式)。此名称是从getTestName()返回的。我观察到的是getTestName()在我的@Test之前和之后被调用。最初,它返回null(为了处理NullPointerException,我返回“”而不是null),后来它返回正确的值。但我没有看到这在报告中得到反映

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

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

共有3个答案

施知
2023-03-14

如果您想更改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();
    }
}
...
...
芮承运
2023-03-14

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

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

因此,我必须实现一个非常定制的getName()。一些假设(在我的特殊情况下):

  1. 只运行三个报告:TestHTML儿女、电子邮件记者、XMLSuiteResultWriter。
  2. 如果由于假设的报告者之一而没有调用get name,则返回当前设置的名称很好。
  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):"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;
}

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

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

--编辑1

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

使用@Factory和@DataProvider进行测试

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

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

董康平
2023-03-14

我也有同样的问题,发现它有助于在用@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()方法返回的值位于测试实例名称属性中,而不是名称属性中。

 类似资料:
  • 我有一个简单的测试套件(对这个例子来说非常简单)。我在构造函数上使用@Factory表示法和一个@DataProvider,它返回一个城市对象[][](多达100个),我运行我的测试。在这个例子中有3个@Test方法。如果我不将测试名称附加到城市名称,我将有一个超文本语言标记报告,它只会一遍又一遍地列出3个测试方法,无法区分它们。 我尝试过这样的例子:这里。 我的测试课做得很好。我遇到的问题是,当

  • 22.13.7.1.TestNG 的参数化方法和报告 TestNG支持参数化方法,允许一个特定的测试方法使用不同的输入被执行多次。Gradle会在测试报告中包含该方法的参数值. 给出一个叫aTestMethod的测试方法,该方法有两个参数,在测试报告中会根据名字报告:aTestMethod(toStringValueOfParam1, toStringValueOfParam2). 这很容易识别的

  • 背景: null > @test(groups={“init”})public void openURL() 包含用于启动webdriver并使用给定URL打开chrome>实例的webdriver代码。 @test(dependsonGroups={“init”})public void testLogin() 包含的webdriver代码指向: 1。找到用户名密码文本输入元素,从属性文件中输入

  • 我用Selenium创建了测试框架,并为测试报告设置了ExtentReports。对字段使用页面对象模型和@FindBy注释为每个页面创建自己的WebElements存储。现在我想创建自定义注释@Name 以及它的实现,以便能够在我的报告后面使用每个WebElement的描述。我有我自己的实现点击()方法 我能够得到所有元素的描述注释类与反射像这里 是否可以在java中读取注释的值? 但无法获取我

  • 我在java testng测试中使用allure testng(2.12.1)适配器。我有使用@DataProvider的测试。我的测试实现了ITest,以在运行时更改测试方法实例名称。当我运行测试时,我看到了不同的测试方法名称,但在allure报告中,它为每个测试运行显示了相同的测试方法。如何配置诱惑报告以显示类似IDE的内容? 预期:测试一测试二测试三 实际:myTest myTest myT

  • 我正在进行跨浏览器测试,在4种浏览器Chrome、Firefox、IE和Safari上运行多个类中的每个测试方法。 testng HTML报告 即使testng HTML报告会有浏览器名称,但与测试方法相对应也很好。 我找到了这个链接,但我只需要浏览器列旁边的方法列到自定义报告中的链接