51Testing软件测试论坛

标题: JUnit4 多线程执行测试用例 (上) [打印本页]

作者: 八戒你干嘛    时间: 2017-8-8 11:14
标题: JUnit4 多线程执行测试用例 (上)
本帖最后由 八戒你干嘛 于 2017-8-8 11:18 编辑

前言:
之前发过类似的文章,现重新调整了部分格式,部分内容稍作调整和添加,便于阅读。
评论中,有人说直接使用TestNG,就可以实现多线程,是的,但是方式不一样;我们是按照自己的需求对JUnit4自定义多线程Runner,直接在某个类加上相应的注解即可,运行该类就行,支持类和方法级别;TestNG只在方法上有注解 @Test(threadPoolSize = m, invocationCount = n, timeOut = i)实现了对这个方法进行多线程重复跑,threadPoolSize多少个线程执行该方法,invocationCount被执行次数,timeOut每次执行该方法的超时时间,这仅是用多线程重复执行这一个方法,而不是类下面的所有方法同时并发执行,并不是所谓的方法级别并发;TestNG是在xml指定并发的类,方法,组件,具体参照TestNG Executing Parallel Tests Example
这里不讨论TestNG与JUnit4谁好谁坏,JUnit 4 vs TestNG,只要能满足自己的业务需要即可。
本文仅针对JUnit4进行二次开发。
JUnit4本身是支持多线程,但没有提供多线程的注解;本文将介绍JUnit4自身的多线程实现,自定义对单个类进行多线程执行的Runner和自定义聚合多个类进行多线程执行的Runner。
(一)JUnit4自身的多线程实现JUnit4提供了ParallerComputer类来使用多线程执行测试用例。
java.lang.Object
  extended by org.junit.runner.Computer
      extended by org.junit.experimental.ParallelComputer
源码如下:
  1. 001    package org.junit.experimental;
  2. 002   
  3. 003    import java.util.concurrent.ExecutorService;
  4. 004    import java.util.concurrent.Executors;
  5. 005    import java.util.concurrent.TimeUnit;
  6. 006   
  7. 007    import org.junit.runner.Computer;
  8. 008    import org.junit.runner.Runner;
  9. 009    import org.junit.runners.ParentRunner;
  10. 010    import org.junit.runners.model.InitializationError;
  11. 011    import org.junit.runners.model.RunnerBuilder;
  12. 012    import org.junit.runners.model.RunnerScheduler;
  13. 013   
  14. 014    public class ParallelComputer extends Computer {
  15. 015        private final boolean classes;
  16. 016   
  17. 017        private final boolean methods;
  18. 018   
  19. 019        public ParallelComputer(boolean classes, boolean methods) {
  20. 020            this.classes = classes;
  21. 021            this.methods = methods;
  22. 022        }
  23. 023   
  24. 024        public static Computer classes() {
  25. 025            return new ParallelComputer(true, false);
  26. 026        }
  27. 027   
  28. 028        public static Computer methods() {
  29. 029            return new ParallelComputer(false, true);
  30. 030        }
  31. 031   
  32. 032        private static Runner parallelize(Runner runner) {
  33. 033            if (runner instanceof ParentRunner) {
  34. 034                ((ParentRunner<?>) runner).setScheduler(new RunnerScheduler() {
  35. 035                    private final ExecutorService fService = Executors.newCachedThreadPool();
  36. 036   
  37. 037                    public void schedule(Runnable childStatement) {
  38. 038                        fService.submit(childStatement);
  39. 039                    }
  40. 040   
  41. 041                    public void finished() {
  42. 042                        try {
  43. 043                            fService.shutdown();
  44. 044                            fService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
  45. 045                        } catch (InterruptedException e) {
  46. 046                            e.printStackTrace(System.err);
  47. 047                        }
  48. 048                    }
  49. 049                });
  50. 050            }
  51. 051            return runner;
  52. 052        }
  53. 053        //  类的维度
  54. 054        @Override
  55. 055        public Runner getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes)
  56. 056                throws InitializationError {
  57. 057            Runner suite = super.getSuite(builder, classes);
  58. 058            return this.classes ? parallelize(suite) : suite;
  59. 059        }
  60. 060        // 方法的维度
  61. 061        @Override
  62. 062        protected Runner getRunner(RunnerBuilder builder, Class<?> testClass)
  63. 063                throws Throwable {
  64. 064            Runner runner = super.getRunner(builder, testClass);
  65. 065            return methods ? parallelize(runner) : runner;
  66. 066        }
  67. 067    }
复制代码
ParallelComputer类中parallelize(Runner runner)方法重写了
ParentRunner类的方法runner.setScheduler(RunnerSchedulerscheduler) ,重新定义了调度顺序,定义了一个线程池 private final ExecutorService fService = Executors.newCachedThreadPool()来多线程执行,运行结束后finished(),关闭线程池fService.shutdown(),并返回该runner。
其中ParallelComputer类重写了父类 Computer的getSuite()和getRunner:
  1. @Override
  2. public Runner getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes)
  3.         throws InitializationError {
  4.     Runner suite = super.getSuite(builder, classes);
  5.     return this.classes ? parallelize(suite) : suite;
  6. }

  7. @Override
  8. protected Runner getRunner(RunnerBuilder builder, Class<?> testClass)
  9.        throws Throwable {
  10.    Runner runner = super.getRunner(builder, testClass);
  11.     return methods ? parallelize(runner) : runner;
  12. }
复制代码
getSuite()和getRunner()根据ParallelComputer类的全局final变量classes和methods的值去决定是否多线程执行;
classes为true时,并发以类为维度,如下:
  1. return this.classes ? parallelize(suite) : suite;
复制代码
methods为true时,并发以方法为维度,如下:
  1. return methods ? parallelize(runner) : runner;
复制代码
ParallelComputer类提供了带参的构造函数:
  1. public ParallelComputer(boolean classes, boolean methods)
复制代码
可以在类初始化时,直接定义多线程执行(不同维度)的对象。
JUnitCore类中的方法runClasses():
  1. public static Result runClasses(Computer computer,Class<?>... classes)
复制代码
可以在main()函数里直接运行测试用例,参数Computer是ParallelComputer的父类,可以直接new ParallelComputer(boolean classes, boolean methods)对象作为第一个形参。

实例1:
  1. public class A {

  2.     @Test
  3.     public void a() {
  4.         assertThat(3, is(1));
  5.     }

  6.     @Test
  7.     public void b() {
  8.         assertThat(3, not(1));
  9.     }

  10. }

  11. public class B {

  12.     @Test
  13.     public void c() {
  14.         assertThat(3, greaterThan(1));
  15.     }

  16.     @Test
  17.     public void d() {
  18.         assertThat(3, lessThan(1));
  19.     }

  20. }

复制代码
  1. public class ParallelTest {

  2.     public static void main(String[] args) {
  3.         Class[] cls = { A.class, B.class };

  4.         Result rt;

  5.         // 并发以类为维度
  6.         // rt = JUnitCore.runClasses(ParallelComputer.classes(), cls);

  7.         // 并发以方法为维度
  8.         // rt = JUnitCore.runClasses(ParallelComputer.methods(), cls);

  9.         // 并发以类和方法为维度
  10.         rt = JUnitCore.runClasses(new ParallelComputer(true, true), cls);

  11.         System.out.println(rt.getRunCount() + " " + rt.getFailures()  + " " + rt.getRunTime());
  12.     }

  13. }
复制代码
// A,B两个类并发执行,但类的方法还是串行执行;
JUnitCore.runClasses(ParallelComputer.classes(), cls);
// A,B两个类串行执行,但类的方法并发执行
JUnitCore.runClasses(ParallelComputer.methods(), cls);
// A,B两个类并发执行,其方法也并发执行
JUnitCore.runClasses(new ParallelComputer(true, true), cls);
(二)自定义对单个类进行多线程执行的Runner
  1. package com.weibo.concurrent;

  2. import java.util.concurrent.atomic.AtomicInteger;

  3. import org.junit.runner.notification.RunNotifier;
  4. import org.junit.runners.BlockJUnit4ClassRunner;
  5. import org.junit.runners.model.FrameworkMethod;
  6. import org.junit.runners.model.InitializationError;
  7. import org.junit.runners.model.Statement;

  8. /**
  9. * Runs all tests in parallel and waits for them to complete.
  10. *
  11. */
  12. public class MultiThreadedRunner extends BlockJUnit4ClassRunner {

  13.     private AtomicInteger numThreads;

  14.     public static int maxThreads = 10;

  15.     public MultiThreadedRunner (Class<?> klass) throws InitializationError {
  16.         super (klass);
  17.         numThreads = new AtomicInteger(0);
  18.     }

  19.     // Runs the test corresponding to child,which can be assumed to be an element of the list returned by getChildren()
  20.     @Override
  21.     protected void runChild(final FrameworkMethod method, final RunNotifier notifier) {
  22.         while (numThreads.get() > maxThreads) {
  23.             try {
  24.                 Thread.sleep(1000);
  25.             } catch (InterruptedException e) {
  26.                 System.err.println ("Interrupted: " + method.getName());
  27.                 e.printStackTrace();
  28.                 return; // The user may have interrupted us; this won't happen normally
  29.             }
  30.         }
  31.         numThreads.incrementAndGet();
  32.         // 用线程执行父类runChild(method, notifier)
  33.         new Thread (new Test(method, notifier)).start();
  34.     }

  35.     // childrenInvoker() call runChild(Object, RunNotifier) on each object returned by getChildren()
  36.     // evaluate() run the action, 调用父类BlockJUnit4ClassRunner的evaluate()
  37.     @Override
  38.     protected Statement childrenInvoker(final RunNotifier notifier) {
  39.         return new Statement() {
  40.             @Override
  41.             public void evaluate() throws Throwable {
  42.                 MultiThreadedRunner.super.childrenInvoker(notifier).evaluate();
  43.                 // wait for all child threads (tests) to complete
  44.                 while (numThreads.get() > 0) {
  45.                     Thread.sleep(1000);
  46.                 }
  47.             }
  48.         };
  49.     }

  50.     class Test implements Runnable {
  51.         private final FrameworkMethod method;
  52.         private final RunNotifier notifier;

  53.         public Test (final FrameworkMethod method, final RunNotifier notifier) {
  54.             this.method = method;
  55.             this.notifier = notifier;
  56.         }

  57.         @Override
  58.         public void run () {
  59.             System.err.println (method.getName());
  60.             MultiThreadedRunner.super.runChild(method, notifier);
  61.             numThreads.decrementAndGet();
  62.         }
  63.     }

  64. }

复制代码

由于字数限制:请转至
JUnit4 多线程执行测试用例 (下)


作者: 草帽路飞UU    时间: 2017-8-8 13:33
可以用testng来实现并发测试,加一个注解就OK了




欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2