TestNG:如何多次运行相同的测试用例?

36

我想多次运行一个测试用例。在 testng.xml 中是否可配置?如果我在测试方法中添加循环,则每次运行的结果将不会受到 testng 报告的影响。

我想要运行一个测试用例多次。这是否可以通过 testng.xml 进行配置?如果我在测试方法中添加循环,那么每次运行的结果将不会反映在 testng 报告中。请注意保留原内容中的 HTML 标签。

1
我如何在运行其他测试方法的同时多次运行同一个测试? - Emna Ayadi
8个回答

54

你无法从xml中实现此操作,但在@Test注释中,您可以添加一个invocationCount属性,指定要运行的次数。这将在报告中显示为运行了这么多次测试。

例如:

@Test(invocationCount = 10)
public void testCount() {..}

你在结尾处漏掉了闭合的括号,需要做一些小修改。


这太糟糕了,因为能够在XML中配置而不是在代码中编写对于某些用例非常重要。例如:我有一个功能测试用例purchaseXYZ()。在我的功能测试套件中,我只想运行它一次以查看是否有任何问题。在我的性能测试套件中,我想运行它100次以获取平均延迟时间。因此,我需要能够从XML中指定调用次数,而不是在代码中硬编码。 - Thomas Nguyen
1
为什么不直接再做一个测试呢——一个用于功能测试,另一个用于单元测试? - fIwJlxSzApHEZIl
虽然我能看出那样做可以起作用,但它更像是一种变通方法而不是解决方案。 - John Chesshir
@JohnChesshir 最好的解决方案通常是最容易实现的变通方法,因为似乎总有更大的问题需要解决。 - fIwJlxSzApHEZIl
@anon58192932 确实。不过,正如你可以看到的,我非常注重细节,就像我的回答一样。 - John Chesshir

9

TestNg有一个方法。您可以使用此方法多次运行测试用例:

@Test(invocationCount = 100)

public void testCount() {

}

1
你能否请修改你的回答并澄清你的意思?目前我无法理解你是在提供新的答案还是在评论niharika_neo的答案。如果你想询问一些关于它的问题,你应该在一个新的问题中提出,而不是在这里。这是一个问答网站,而不是一个论坛。谢谢! - Fabio says Reinstate Monica

7

迄今为止,没有一个答案真正给用户在testng文件中增加调用计数的能力,这正是被要求的。本解决方案依赖于gaurav25的DataProvider解决方案。

class TestClass() {
    int invocations;

    @Parameters({ "invocationCount" })
    @BeginClass
    void BeginClass( @Optional("1") String invocationCount) {
        this.invocations = Ingeter.parse(invocationCount)
    }

    // It will return a 2D array of size 3x1
    @DataProvider(name="URLprovider")
    private Object[][] getURLs() {
        ArrayList<Object []> obj = new ArrayList<>(3 * this.invocations);

        for(int iCount = 0; iCount < this.invocations; ++iCount) {
            list.add( new Object[] {"https://www.google.co.in/"} );
            list.add( new Object[] {"http://www.gmail.com/"} );
            list.add( new Object[] {"http://stackoverflow.com/"} );
        }

        return list.toArray();
    }

    /* Since Data provider for this test method returns 2D array of size
     (3*invocations)x1, this test method will run 3*invocations 
     times **automatically** with 1 parameter every time. */
    @Test(dataProvider="URLprovider")
    private void notePrice(String url) {
        driver.get(url);
        System.out.println(driver.getTitle());  
    }
}

现在,您可以通过这个testng.xml文件来改变测试函数运行的测试集数量:
<suite name="ESFService" verbose="1" parallel="methods" thread-count="1" data-provider-thread-count="10" >
    <test name="Basic">
        <classes>
            <class name="TestClass">
                <parameter name="invocationCount" value="5"/>
            </class>
        </classes>
    </test>
</suite>

在这段代码中,我最不喜欢的是同时使用相同的变量名 String invocationCount 和 int invocationCount。这总是会导致混淆和可能的错误。而且你的方法 getURls() 列表未被定义。 - JPM
@JPM 已经注意到了 invocationCount 的问题。我已经将成员变量和所有使用它的地方都改为了 "invocations"。 关于 getURLs(),该方法已经被明确定义。我想你可能是想说它从来没有被 "使用"。基于这个假设,虽然该方法从未被直接调用,但它通过分配给 DataProvider 注释而被使用。请注意,该注释将属性 "name" 设置为 "URLprovider"。然后,Test 注释在 notePrice 函数上通过将其 dataProvider 属性设置为相同的值来引用该值。 - John Chesshir

2

您可以在testngSuite中添加多个测试并执行。 在所有测试下,类名应相同,以便执行相同的脚本多次。


1
整个问题似乎很老,但以防万一有人仍在寻找(我就是),这就是答案。它全部都在XML中。只需创建多个<test></test>即可。 - Rob G

2
我知道可能有点晚了,但如果你的目标是为每次运行生成报告,那么你可以尝试使用TestNG监听器IAnnotationTransformer。
代码片段:
public class Count implements IAnnotationTransformer {

    @Override
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
        // TODO Auto-generated method stub
        annotation.setInvocationCount(numberOfTimesTOExecute);
    }

XML片段

<listeners>
<listener class-name="multiple.Count"></listener>


这看起来非常好。但是你能否从testng.xml文件中填充numberOftimesTOExecute变量? - John Chesshir
可以创建一个"服务加载器"。参见此问题的答案:https://stackoverflow.com/questions/45593426/testng-set-invocationcount-globally - Andrejs

1
你无法从xml中实现它,但可以通过在TestNG中使用@DataProvider注释来实现。

这是一个示例代码:

/* Since Data provider for this test method returns 2D array of size 3x1, 
this test method will run 3 times **automatically** with 1 parameter every time. */
@Test(dataProvider="URLprovider")
private void notePrice(String url) {
    driver.get(url);
    System.out.println(driver.getTitle());  
}

// It will return a 2D array of size 3x1
@DataProvider(name="URLprovider")
private Object[][] getURLs() {
  return new Object[][] {
    {"https://www.google.co.in/"},
    {"http://www.gmail.com/"},
    {"http://stackoverflow.com/"}
  };
}

1
public class ProcessTest implements ITest {


    protected ProcessData processData;

    @Test
    public void executeServiceTest() {
        System.out.println(this.processData.toString());
    }

    @Factory(dataProvider = "processDataList")
    public RiskServiceTest(ProcessData processData) {
        this.processData = processData;
    }

    @DataProvider(name = "processDataList", parallel=true)
    public static Object[] getProcessDataList() {

        Object[] serviceProcessDataList = new Object[10];

    for(int i=0; i<=serviceProcessDataList.length; i++){
        ProcessData processData = new ProcessData();
        serviceProcessDataList[i] = processData
    }


        return serviceProcessDataList;
    }

    @Override
    public String getTestName() {

        return this.processData.getName();
    }
}

通过使用TestNG的@Factory和@DataProvider注释,您可以多次执行相同的测试用例,并使用不同的数据。

0
如果您不介意使用Sprint,您可以创建这个类:
package somePackage;

import org.junit.Ignore;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.springframework.test.annotation.Repeat;

public class ExtendedRunner extends BlockJUnit4ClassRunner {

    public ExtendedRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected Description describeChild(FrameworkMethod method) {
        if (method.getAnnotation(Repeat.class) != null
                && method.getAnnotation(Ignore.class) == null) {
            return describeRepeatTest(method);
        }
        return super.describeChild(method);
    }

    private Description describeRepeatTest(FrameworkMethod method) {
        int times = method.getAnnotation(Repeat.class).value();

        Description description = Description.createSuiteDescription(
            testName(method) + " [" + times + " times]",
            method.getAnnotations());

        for (int i = 1; i <= times; i++) {
            description.addChild(Description.createTestDescription(
                getTestClass().getJavaClass(), "[" + i + "] "
                        + testName(method)));
        }
        return description;
    }

    @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);

        if (method.getAnnotation(Repeat.class) != null
                && method.getAnnotation(Ignore.class) == null) {
            runRepeatedly(methodBlock(method), description, notifier);
        }
        super.runChild(method, notifier);
    }

    private void runRepeatedly(Statement statement, Description description,
            RunNotifier notifier) {
        for (Description desc : description.getChildren()) {
            runLeaf(statement, desc, notifier);
        }
    }

}

然后在实际测试中:

package somePackage;

import *.ExtendedRunner;

import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.Repeat;

@Ignore
@RunWith(ExtendedRunner.class)
public class RepeatedTest{
    @Repeat(value N)
    public void testToBeRepeated() {

    }
}

其中N是您想要测试重复的次数。


我正在使用TestNG和数据提供程序。我该怎么办?现在我正在操作对象数组的大小。你认为这是一个好主意吗? - Feixiong Liu
我想你的意思是“如果你不介意使用Spring……”。另外请注意,这是关于TestNG而不是JUnit的问题。 - John Chesshir

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