【问题标题】:Running JUnit Test in parallel on Suite Level?在套件级别上并行运行 JUnit 测试?
【发布时间】:2011-04-15 09:24:09
【问题描述】:

我有一堆在 JUnit 测试套件中组织的测试。这些测试极大地利用 selenium 来测试 Web 应用程序。因此,对于 selenium,这些测试的运行时间很长。由于测试数据库中有一些重叠,套件中的测试类无法并行运行,我想并行运行这些套件。

JUnit ParallelComputer 只能在类或方法级别并行执行测试,JUnit 是否有任何标准方法可以通过套件执行此操作?

如果我只是将套件类传递给 junit 运行器并将计算机配置为在类级别并行化,它会选择测试类本身,而不是套件。

br 弗兰克

【问题讨论】:

    标签: java junit automated-tests parallel-processing suite


    【解决方案1】:

    这是一些对我有用的代码。我没有写这个。如果您使用 @RunWith(ConcurrentSuite.class) 而不是 @RunWith(Suite.class) 它应该可以工作。下面还需要一个注释。

    package utilities.runners;
    
    import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
    import org.junit.runner.Runner;
    import org.junit.runners.Suite;
    import org.junit.runners.model.InitializationError;
    import org.junit.runners.model.RunnerBuilder;
    import org.junit.runners.model.RunnerScheduler;
    
    import utilities.annotations.Concurrent;
    
    import java.util.Arrays;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Queue;
    import java.util.concurrent.CompletionService;
    import java.util.concurrent.ExecutorCompletionService;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @author Mathieu Carbou (mathieu.carbou@gmail.com)
     */
    public final class ConcurrentSuite extends Suite {
        public ConcurrentSuite(final Class<?> klass) throws InitializationError {
            super(klass, new AllDefaultPossibilitiesBuilder(true) {
                @Override
                public Runner runnerForClass(Class<?> testClass) throws Throwable {
                    List<RunnerBuilder> builders = Arrays.asList(
                            new RunnerBuilder() {
                                @Override
                                public Runner runnerForClass(Class<?> testClass) throws Throwable {
                                    Concurrent annotation = testClass.getAnnotation(Concurrent.class);
                                    if (annotation != null)
                                        return new ConcurrentJunitRunner(testClass);
                                    return null;
                                }
                            },
                            ignoredBuilder(),
                            annotatedBuilder(),
                            suiteMethodBuilder(),
                            junit3Builder(),
                            junit4Builder());
                    for (RunnerBuilder each : builders) {
                        Runner runner = each.safeRunnerForClass(testClass);
                        if (runner != null)
                            return runner;
                    }
                    return null;
                }
            });
            setScheduler(new RunnerScheduler() {
                ExecutorService executorService = Executors.newFixedThreadPool(
                        klass.isAnnotationPresent(Concurrent.class) ?
                                klass.getAnnotation(Concurrent.class).threads() :
                                (int) (Runtime.getRuntime().availableProcessors() * 1.5),
                        new NamedThreadFactory(klass.getSimpleName()));
                CompletionService<Void> completionService = new ExecutorCompletionService<Void>(executorService);
                Queue<Future<Void>> tasks = new LinkedList<Future<Void>>();
    
                @Override
                public void schedule(Runnable childStatement) {
                    tasks.offer(completionService.submit(childStatement, null));
                }
    
                @Override
                public void finished() {
                    try {
                        while (!tasks.isEmpty())
                            tasks.remove(completionService.take());
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    } finally {
                        while (!tasks.isEmpty())
                            tasks.poll().cancel(true);
                        executorService.shutdownNow();
                    }
                }
            });
        }
    
        static final class NamedThreadFactory implements ThreadFactory {
            static final AtomicInteger poolNumber = new AtomicInteger(1);
            final AtomicInteger threadNumber = new AtomicInteger(1);
            final ThreadGroup group;
    
            NamedThreadFactory(String poolName) {
                group = new ThreadGroup(poolName + "-" + poolNumber.getAndIncrement());
            }
    
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(group, r, group.getName() + "-thread-" + threadNumber.getAndIncrement(), 0);
            }
        }
    
    }
    

    并且注解如下。

    package utilities.annotations;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author Mathieu Carbou (mathieu.carbou@gmail.com)
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE })
    public @interface Concurrent {
        int threads() default 5;
    }
    

    【讨论】:

    【解决方案2】:

    由于 Suite 是用来注解 Class 的,所以以 JUnitCore.runClasses(ParallelComputer.classes(), cls) 的方式运行 Suite-annotated 类。 cls 是套件注释的类。

    @RunWith(Suite.class)
    @Suite.SuiteClasses({
    Test1.class,
    Test2.class})
    public class Suite1 {
    }
    
    @RunWith(Suite.class)
    @Suite.SuiteClasses({
    Test3.class,
    Test4.class})
    public class Suite2 {
    }
    ...
    JUnitCore.runClasses(ParallelComputer.classes(), new Class[]{Suite1.class, Suite2.class})
    

    【讨论】:

    • 您好,感谢您的快速回答。可悲的是,这并没有按照我的预期运行测试。就像我在问题末尾所写的那样,这会并行运行套件内部的测试类,而不是套件本身。
    • 您的示例确实执行以下方式:Test1 和 Test2 并行运行,这正是我必须防止的行为。
    • 欢迎您!如果我的代码不能帮助您,您可以编写多线程来运行您的套件(如果没有更好的解决方案)。
    • 我正在考虑这个,但如果我这样做,我必须自己汇总 junit 结果,因为我需要它作为测试报告。但感谢您的快速帮助!
    猜你喜欢
    • 2011-11-12
    • 2011-04-29
    • 2012-12-04
    • 2020-02-27
    • 1970-01-01
    • 2022-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多