如何在单一方法测试和可排序测试数据的情况下订购TestNG Factory执行?

时间:2014-08-07 00:19:25

标签: java testng

首先,您应该知道我正在使用TestNG v6.8.8和Java JDK 6.我遇到了在不同版本的Linux和Mac OS 10.9.4上运行的问题。转到代码清单。

工厂类

import org.testng.annotations.Factory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author rcourtright
 *         Date: 8/5/14
 *         Time: 4:50 PM
 */
public class ErsatzFactory {

    private final List<String> testData;

    public ErsatzFactory() {
        testData = new ArrayList<String>();
        int order = 0;
        for (int i = 0 ; i < 9; i++) {
            testData.add(order++ + "-Test");
        }
        Collections.sort(testData);
    }

    @Factory
    public Object[] setUp() {
        List<ErsatzTest> objects = new ArrayList<ErsatzTest>();
        int order = 0;
        for (String testDatum : testData) {
            objects.add(
                    order,
                    new ErsatzTest(testDatum)
            );
            order++;
        }
        return objects.toArray();
    }

}

测试类

import org.testng.ITest;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

/**
 * @author rcourtright
 *         Date: 8/5/14
 *         Time: 4:45 PM
 */
//@Listeners(ErsatzListener.class)
public class ErsatzTest implements ITest {

    private String order;

    public ErsatzTest(String order) {
        this.order = order;
    }

    @Test
    public void justDoIt() {
        System.out.println(order);
    }

    @Override
    public String getTestName() {
        return order;
    }
}

TestNG XML

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">

<suite name="ersatz-testing" verbose="1" >
    <test name="ersatz-test-factory" preserve-order="true">
        <classes>
            <class name="ErsatzFactory"/>
        </classes>
    </test>
</suite>

所以你可以在我的构造中看到我有一个自然顺序的列表,我对该集合进行排序。工厂的工作是按顺序执行单个测试方法。通过调试,我发现Factory返回的集合按所需顺序排列。但这不是他们的执行方式。

使用TestNG支持的ant目标,这是执行顺序:

检测结果
ersatz-test-executor:
   [testng] 5-Test
   [testng] 2-Test
   [testng] 7-Test
   [testng] 6-Test
   [testng] 4-Test
   [testng] 8-Test
   [testng] 1-Test
   [testng] 0-Test
   [testng] 3-Test
   [testng]
   [testng] ===============================================
   [testng] ersatz-testing
   [testng] Total tests run: 9, Failures: 0, Skips: 0
   [testng] ===============================================
   [testng]

这是意料之外的,也是不可取的。很明显,这个测试是微不足道的,但它是一个相当复杂的系统测试的代理,我必须得到正确的执行顺序。

虽然这里没有列出,但我必须使用TestNG Listener进行后期测试结果处理,看来我必须使用Factory而不是独立的DataProvider。 (我已经在测试类声明中注释掉了Listener注释。)我应该注意Listener的工作没有发生。

因为只有一种方法,所以我不能使用优先级注释或方法依赖性。测试完全由数据驱动。数据是可排序的,如果内存服务,当我在测试类中使用DataProvider时,我得到了该顺序。但我也需要Listener来装饰测试结果报告,这促使我使用工厂。我应该注意,无论我是否使用监听器,都存在执行问题。如果您编译并运行此代码,我希望您会看到列出的结果似乎是随机顺序。

提前感谢您考虑此问题。

1 个答案:

答案 0 :(得分:5)

好的,我怀疑了。我不理解方法拦截器的预处理能力。所以我创建了一个,我得到了我的有序测试。为了寻求解决方案的任何人的利益,我将在此处列出我的代码更改。

方法拦截器

import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author rcourtright
 *         Date: 8/11/14
 *         Time: 2:37 PM
 */
public class ErsatzMethodInterceptor implements IMethodInterceptor {

    @Override
    public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
        Map<String,IMethodInstance> orders = new TreeMap<String,IMethodInstance>();
        for (IMethodInstance iMethodInstance : methods) {
            if (!iMethodInstance.getMethod().getMethodName().equals("justDoIt")) {
                continue;
            }
            Object obj = iMethodInstance.getInstance();
            if (!(obj instanceof ErsatzTest)) {
                continue;
            }
            ErsatzTest ersatzTest = (ErsatzTest)obj;
            String order = ersatzTest.getOrder();
            orders.put(order,iMethodInstance);
        }
        List<IMethodInstance> tests = new ArrayList<IMethodInstance>(orders.size());
        for (String order : orders.keySet()) {
            IMethodInstance test = orders.get(order);
            tests.add(test);
        }
        return tests;
    }
}

将测试类连接到拦截器类

    import org.apache.log4j.Logger;
import org.testng.ITest;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

/**
 * @author rcourtright
 *         Date: 8/5/14
 *         Time: 4:45 PM
 */
@Listeners(
    {
        ErsatzMethodInterceptor.class,
        ErsatzListener.class
    }
)
public class ErsatzTest implements ITest {
    private Logger logger = Logger.getLogger(ErsatzTest.class);

    private String order;

    public ErsatzTest(String order) {
        this.order = order;
    }

    public String getOrder() {
        return order;
    }

    @Test
    public void justDoIt() {
        logger.info(order);
    }

    @Override
    public String getTestName() {
        return order;
    }
}

奖励听众将漂亮的打印结果打印到报告

    import org.apache.log4j.Logger;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import org.testng.internal.BaseTestMethod;
import org.testng.internal.TestResult;

import java.lang.reflect.Field;

/**
 * @author rcourtright
 *         Date: 7/29/14
 *         Time: 12:03 PM
 */
public class ErsatzListener extends TestListenerAdapter {

    private Logger logger = Logger.getLogger(ErsatzListener.class);

    private void setTestNameInXml(ITestResult tr) {
        try {
            Field mMethod = TestResult.class.getDeclaredField("m_method");
            mMethod.setAccessible(true);
            mMethod.set(tr, tr.getMethod().clone());
            Field mMethodName = BaseTestMethod.class.getDeclaredField("m_methodName");
            mMethodName.setAccessible(true);
            mMethodName.set(tr.getMethod(), tr.getTestName());
        } catch (IllegalAccessException ex) {
            logger.error(ex.getLocalizedMessage(), ex);
        } catch (NoSuchFieldException ex) {
            logger.error(ex.getLocalizedMessage(), ex);
        }
    }

    @Override
    public void onTestSuccess(ITestResult tr) {
        setTestNameInXml(tr);
        super.onTestSuccess(tr);
    }

    @Override
    public void onTestFailure(ITestResult tr) {
        setTestNameInXml(tr);
        super.onTestFailure(tr);
    }

    @Override
    public void onTestSkipped(ITestResult tr) {
        setTestNameInXml(tr);
        super.onTestSkipped(tr);
    }

}
测试执行结果

我假设你现在有了log4j,所以在类路径上只有一个可用的log4j.xml,这里我们订购了测试结果:

$ java org.testng.TestNG testng.xml
[TestNG] Running:
  /Users/rcourtright/Desktop/ersatz/testng.xml


0 [main] INFO ErsatzTest  - 0-Test

2 [main] INFO ErsatzTest  - 1-Test

3 [main] INFO ErsatzTest  - 2-Test

4 [main] INFO ErsatzTest  - 3-Test

5 [main] INFO ErsatzTest  - 4-Test

6 [main] INFO ErsatzTest  - 5-Test

7 [main] INFO ErsatzTest  - 6-Test

8 [main] INFO ErsatzTest  - 7-Test

9 [main] INFO ErsatzTest  - 8-Test

===============================================
ersatz-testing
Total tests run: 9, Failures: 0, Skips: 0
===============================================

最后,我没有意识到方法拦截器的强大功能,并且它在工厂做任何事情之后运行。也就是说,工厂方法不控制顺序,但可以通过使用拦截器来改变它。