【问题标题】:Is there a way to give a specific name to test case using TestNG data providers?有没有办法使用 TestNG 数据提供者为测试用例指定一个特定的名称?
【发布时间】:2021-08-30 12:11:50
【问题描述】:

我正在使用 @DataProvider 使用 TestNG 6.14.3 在 Java 中编写单元测试

我想要达到的目标:

  1. 我想为每个数据提供者案例命名。
  2. 我不想将名称作为参数之一,而是自定义名称。 (仅举个例子:“Good flow no auth”)

我尝试过的:

  1. 我添加了一个额外的参数并遵循了这个guide, 问题是现在我在每个我不想要的测试中都有一个未使用的参数caseName

  2. 创建一个忽略第一个参数的自定义DataProvider 注释,这不起作用,因为我找不到将它与TestNG 集成的方法。

我的问题:

  1. 是否有内置方法可以为测试用例指定特定名称?
  2. 如果对 1 的回答为“否”:有没有办法集成自定义 DataProvider 或在将数据提供给测试本身之前拦截数据?
  3. 即使在关注guide 之后,我仍然在 Intellij 中看到其他参数作为测试名称后缀,这正常吗?如果没有,我将发布我的示例代码。

谢谢!

【问题讨论】:

    标签: java unit-testing testng testng-dataprovider


    【解决方案1】:

    一种解决方案可能是在测试类中声明一个String 名称数组,然后按顺序执行。如果您没有并行运行测试,这将起作用。

    否则,您可以使用@Factory 注解,它可以从数据提供者那里获取输入,然后创建测试对象。

    @Factory(dataProvider = "data")
    public Object[] createInstances(String val, String name) {
        return new Object[] { new MyTest(name) };
    }
    
    @DataProvider
    public Object[][] data() {
        return new Object[][] { { "a", "one" }, { "b", "two" } };
    }
    

    因此,您需要在测试类中使用 name 字段,该字段将使用构造函数进行设置。结合link 中建议的代码来设置测试名称,您可以这样做:

    public class MyTest implements ITest {
    
        private String name;
        private ThreadLocal<String> testName = new ThreadLocal<>();
    
        public MyTest() {
        }
    
        public MyTest(String name) {
            this.name = name;
        }
    
        @Factory(dataProvider = "data")
        public Object[] createInstances(String name) {
            return new Object[] { new MyTest(name) };
        }
        
        @DataProvider
        public Object[][] data() {
            return new Object[][] { { "one" }, { "two" } };
        }
    
        @BeforeMethod
        public void beforeMethod(Method method) {
            testName.set(method.getName() + "_" + name);
        }
    
        @Test
        public void test() {
            System.out.println(name);
        }
    
        @Override
        public String getTestName() {
            return testName.get();
        }
    }
    

    编辑:不使用Factory,可以如下实现。这就需要在ITestContext中设置相关细节:

    @DataProvider
    public Object[][] data(ITestContext ctx) {
        ctx.setAttribute("names", new String[]{"one", "two"});
        ctx.setAttribute("index", 0);
        
        return new Object[][] { {"val1", "data1"}, {"val2", "data2"} };
    }
    

    现在在beforeMethod 中也注入ITestContext

    @BeforeMethod
    public void beforeMethod(ITestContext ctx, Method method) {
        testName.set(method.getName() + "_" + getName(ctx));
    }
    
    // Create a helper method getName.
    private static String getName(ITestContext ctx) {
        int index = (int) ctx.getAttribute("index");
        // get the name based on the current index.
        String name = ((String[]) ctx.getAttribute("names"))[index++];
        
        // update the attribute with the incremented index.
        ctx.setAttribute("index", index);
        return name;
    }
    
    @Test(dataProvider = "data") 
    public void yourTest(String val, String data) {
        // .............
    }
    

    注意:当测试并行运行时,这将无法正常工作。

    【讨论】:

    • 感谢您的回答!在您的示例中,我们不再将参数放入测试中...
    • @ItzhakEretzKdosha 你需要在构造函数中这样做
    • 所以参数不会被注入到测试中而是通过ctor添加到局部变量中?
    • @ItzhakEretzKdosha 是的。但是,如果您没有并行运行它,那么您可以采用另一种方法(没有这种工厂方法)。如果你愿意,我可以更新答案。
    • 我想要这样,因为我在同一个文件中使用多个 dataProviders 来测试不同的方面,并希望将它们作为参数提供给测试方法。
    【解决方案2】:

    你可以这样做:

    public class TestName {
    
        @DataProvider(name = "nameAndOthers")
        Iterator<Object[]> provideData(ITestContext testContext){
            Object[][] data = new Object[][]{
                    {"name1", 1, 2},
                    {"name2", 3, 4},
                    {"name3", 5, 6},
            };
            Iterator<Object[]> originalDataIterator = Arrays.asList(data).iterator();
            return new Iterator<Object[]>() {
                @Override
                public boolean hasNext() {
                    return originalDataIterator.hasNext();
                }
    
                @Override
                public Object[] next() {
                    Object[] next = originalDataIterator.next();
                    testContext.getCurrentXmlTest().setName(next[0].toString());
                    return Arrays.copyOfRange(next, 1, next.length);
                }
            };
    
        }
    
        @Test(dataProvider = "nameAndOthers")
        void testName(Integer one, Integer two){
            System.out.println(one + ": " + two);
        }
    
    }
    

    输出将是:

    1: 2
    3: 4
    5: 6
    

    并且测试将从您的数据中取名:

    【讨论】:

    • 感谢您的回答!有没有办法在不为每个数据提供者复制的情况下使用此代码?例如自定义数据提供者?
    • 魔法实际上发生在Iterator 内部。每次 TestNg 获取下一行数据时都会设置测试名称。在我的示例中,此迭代器被创建为匿名类。您可以单独实现您的类并在您的数据提供者中创建一个实例。因此,您不会重复代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-30
    • 2016-04-18
    • 1970-01-01
    相关资源
    最近更新 更多