Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用TestNG使每个测试方法在自己的测试类实例中运行?_Java_Unit Testing_Testing_Testng - Fatal编程技术网

Java 使用TestNG使每个测试方法在自己的测试类实例中运行?

Java 使用TestNG使每个测试方法在自己的测试类实例中运行?,java,unit-testing,testing,testng,Java,Unit Testing,Testing,Testng,因此,我认为以下代码在TestNG中可以很好地运行,但事实并非如此: public class Tests { int i = 0; @Test public void testA() { Assert.assertEquals(0, i); ++i; } @Test public void testB() { Assert.assertEquals(0, i); ++i;

因此,我认为以下代码在TestNG中可以很好地运行,但事实并非如此:

public class Tests {
    int i = 0;

    @Test
    public void testA() {
        Assert.assertEquals(0, i);
        ++i;
    }

    @Test
    public void testB() {
        Assert.assertEquals(0, i);
        ++i;
    }
}

有没有办法让TestNG为每个测试方法启动一个新的测试类?

常见的解决方案是使用@BeforeMethod方法来设置测试状态

@BeforeMethod
public void setup() {
   i = 0; 
}

到目前为止,我发现这个问题最常见的解决方案是使用ThreadLocal,只需处理每个测试类只有一个实例的事实。这涉及如何处理并行/线程测试的所有问题。这是可行的,但有点难看

private ThreadLocal<Integer> i = new ThreadLocal<>();

@BeforeMethod
public void setup() {
    i.set(0);
}

@Test
public void testA() {
    Integer i1 = i.get();
    Assert.assertEquals(0, i.get().intValue());
    i.set(i1 + 1);
}

@Test
public void testB() {
    Integer i1 = i.get();
    Assert.assertEquals(0, i.get().intValue());
    i.set(i1 + 1);
}
我现在创建了两个
测试
实例,由testNG运行

然后问题是您的测试仍然失败,因为它将尝试在您的测试类上运行所有测试方法。为了解决这个问题,您可以实现一个IMethodInterceptor,并将一个解决方案整合在一起,以强制每个测试实例只运行一个方法。维护一个方法列表,并一次浏览一个

这是一个我拼凑的野蛮例子

public class TestFactory implements IMethodInterceptor {
    private List<String> methodsToRun = new ArrayList<>();
    private List<Object> testInstances = new ArrayList<>();

    @Factory
    public Object[] factory(){
        return new Object[]{new Tests(), new Tests()};
    }

    @Override
    public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
        ArrayList<IMethodInstance> tempList = new ArrayList<>();
        for(IMethodInstance i: methods){
            if(testInstances.contains(i.getInstance())){
                continue;
            }
            String mName = i.getMethod().getConstructorOrMethod().getName();
            if(!methodsToRun.contains(mName)){
                tempList.add(i);
                methodsToRun.add(mName);
                testInstances.add(i.getInstance());
            }
        }
        return tempList;
    }
}
您可以通过在工厂中动态创建测试的新实例来改进这一点。还将侦听器分解到它自己的文件和许多其他改进中,但您得到了要点


也许像上面这样疯狂的解决方案会对你或其他人有效

你想这样做有什么特别的原因吗?是的,因为我想运行几个与彼此无关的不同测试!我不想保留他们之间的状态!此外,如果它保持状态,我怎么知道哪些测试是第一个运行的,哪些是第二个或第三个?如果我想测试我的堆栈实现,那将需要20个单元测试,您是否希望有人创建20个测试类?“如果您想运行彼此无关的测试,为什么不将它们放在不同的类中?“问题恰恰在于它们之间有很多联系,这就是为什么我希望它们在同一个测试类中,这也是为什么在测试之间清理它们很重要的原因!啊,难道没有更好的方法吗?@Cedric,我对框架有实现两个目的的方法完全没有问题:困扰我的是,它缺乏一种真正的实现单元测试的方法,即在测试之间重置状态。拥有@BeforeMethod充其量只能算是一种黑客行为。例如,一个建议是在TestNG中添加一个@statelementBetweentTests说明(虽然IMO状态重用不应该是默认行为,但这只是我的观点)。换句话说,在单元测试框架中,我倾向于默认行为是在测试之间丢弃状态(即自动)的解决方案虽然我确实理解,对于系统/集成测试,可能会首选相反的方法。剩下的问题是TestNG的主要目标是什么:)@CedricBeust“BeforeMethod”解决方案可能适用于顺序测试,但如何适用于并行测试。让我们假设在上面的例子中有两种并行运行的测试方法。实例变量“i”将具有不可预测的值…不是吗?不知道你是否想到了解决这个问题的办法。我同意阿比吉特的观点@CedricBeust如果需要并行测试的状态,我们应该怎么做?
public class TestFactory implements IMethodInterceptor {
    private List<String> methodsToRun = new ArrayList<>();
    private List<Object> testInstances = new ArrayList<>();

    @Factory
    public Object[] factory(){
        return new Object[]{new Tests(), new Tests()};
    }

    @Override
    public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
        ArrayList<IMethodInstance> tempList = new ArrayList<>();
        for(IMethodInstance i: methods){
            if(testInstances.contains(i.getInstance())){
                continue;
            }
            String mName = i.getMethod().getConstructorOrMethod().getName();
            if(!methodsToRun.contains(mName)){
                tempList.add(i);
                methodsToRun.add(mName);
                testInstances.add(i.getInstance());
            }
        }
        return tempList;
    }
}
@Listeners(TestFactory.class)