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

发布于 2024-11-04 22:20:12 字数 334 浏览 4 评论 0原文

所以我认为下面的代码可以在 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 为每个测试方法启动一个新的 Tests 类?

So I thought the following code would run fine in TestNG, although it doesn't:

public class Tests {
    int i = 0;

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

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

Is there a way to make TestNG fire up a new Tests class for each test method?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

二智少女 2024-11-11 22:20:12

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

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

The common solution is to use an @BeforeMethod method to setup test state,

@BeforeMethod
public void setup() {
   i = 0; 
}
从来不烧饼 2024-11-11 22:20:12

到目前为止,我发现的这个问题最常见的解决方案是使用 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 中遇到的第一个问题。它真的让我发疯了。

如果我忽略您的测试有很多复杂性这一事实,您可能会拼凑出一个解决方案来满足您列出的要求。

TestNG @Factory Factory 允许您创建测试类的新实例。

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

我现在创建了两个 Tests 实例,由 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;
    }
}

然后将侦听器添加到 Tests 类的顶部。

@Listeners(TestFactory.class)

您可以通过在工厂中动态创建测试的新实例来改进这一点。还将侦听器分解为它自己的文件和许多其他改进,但您明白了要点。

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

By far the most common solution to this issue I have found is to use ThreadLocal’s and just deal with the fact that you only have one instance of each Test Class. This deals with all the questions on how to deal with parallel/threaded tests. This works, but is a bit ugly.

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);
}

Now back to the root of your question, new instances for each method.
I’ve been researching for a few weeks similar topics, and I have identified this is the number one issue I was personally having with TestNG. It has literally driven me crazy.

If I was to ignore the fact that your tests had a bunch of complexities, you could potentially hack together a work around to meet the requirements you listed.

A TestNG @Factory Factory allows you to create new instances of your test classes.

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

I’ve now created two Tests instances, to be ran by testNG

Then the issue is your tests still fail, because it will try to run all test methods on your test classes. In order to hack around this you could implement a IMethodInterceptor, and hack together a solution to enforce that each Tests instance only run one method. Maintain a list of methods, and go through them one at a time.

Here is a brute example I hacked together.

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;
    }
}

Then add your listener to the top of your Tests class

@Listeners(TestFactory.class)

You can improve this by dynamically creating new instances of the tests in the factory. Also breaking the listener out into it's own file and numerous other improvements, but you get the gist.

Maybe a crazy solution like the above will work for you or someone else.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文