TA的每日心情 | 无聊 2024-9-19 09:07 |
---|
签到天数: 11 天 连续签到: 2 天 [LV.3]测试连长
|
本帖最后由 八戒你干嘛 于 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
源码如下:
- 001 package org.junit.experimental;
- 002
- 003 import java.util.concurrent.ExecutorService;
- 004 import java.util.concurrent.Executors;
- 005 import java.util.concurrent.TimeUnit;
- 006
- 007 import org.junit.runner.Computer;
- 008 import org.junit.runner.Runner;
- 009 import org.junit.runners.ParentRunner;
- 010 import org.junit.runners.model.InitializationError;
- 011 import org.junit.runners.model.RunnerBuilder;
- 012 import org.junit.runners.model.RunnerScheduler;
- 013
- 014 public class ParallelComputer extends Computer {
- 015 private final boolean classes;
- 016
- 017 private final boolean methods;
- 018
- 019 public ParallelComputer(boolean classes, boolean methods) {
- 020 this.classes = classes;
- 021 this.methods = methods;
- 022 }
- 023
- 024 public static Computer classes() {
- 025 return new ParallelComputer(true, false);
- 026 }
- 027
- 028 public static Computer methods() {
- 029 return new ParallelComputer(false, true);
- 030 }
- 031
- 032 private static Runner parallelize(Runner runner) {
- 033 if (runner instanceof ParentRunner) {
- 034 ((ParentRunner<?>) runner).setScheduler(new RunnerScheduler() {
- 035 private final ExecutorService fService = Executors.newCachedThreadPool();
- 036
- 037 public void schedule(Runnable childStatement) {
- 038 fService.submit(childStatement);
- 039 }
- 040
- 041 public void finished() {
- 042 try {
- 043 fService.shutdown();
- 044 fService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
- 045 } catch (InterruptedException e) {
- 046 e.printStackTrace(System.err);
- 047 }
- 048 }
- 049 });
- 050 }
- 051 return runner;
- 052 }
- 053 // 类的维度
- 054 @Override
- 055 public Runner getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes)
- 056 throws InitializationError {
- 057 Runner suite = super.getSuite(builder, classes);
- 058 return this.classes ? parallelize(suite) : suite;
- 059 }
- 060 // 方法的维度
- 061 @Override
- 062 protected Runner getRunner(RunnerBuilder builder, Class<?> testClass)
- 063 throws Throwable {
- 064 Runner runner = super.getRunner(builder, testClass);
- 065 return methods ? parallelize(runner) : runner;
- 066 }
- 067 }
复制代码 ParallelComputer类中parallelize(Runner runner)方法重写了
ParentRunner类的方法runner.setScheduler(RunnerSchedulerscheduler) ,重新定义了调度顺序,定义了一个线程池 private final ExecutorService fService = Executors.newCachedThreadPool()来多线程执行,运行结束后finished(),关闭线程池fService.shutdown(),并返回该runner。
其中ParallelComputer类重写了父类 Computer的getSuite()和getRunner:
- @Override
- public Runner getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes)
- throws InitializationError {
- Runner suite = super.getSuite(builder, classes);
- return this.classes ? parallelize(suite) : suite;
- }
- @Override
- protected Runner getRunner(RunnerBuilder builder, Class<?> testClass)
- throws Throwable {
- Runner runner = super.getRunner(builder, testClass);
- return methods ? parallelize(runner) : runner;
- }
复制代码 getSuite()和getRunner()根据ParallelComputer类的全局final变量classes和methods的值去决定是否多线程执行;
classes为true时,并发以类为维度,如下:- return this.classes ? parallelize(suite) : suite;
复制代码 methods为true时,并发以方法为维度,如下:
- return methods ? parallelize(runner) : runner;
复制代码 ParallelComputer类提供了带参的构造函数:- public ParallelComputer(boolean classes, boolean methods)
复制代码 可以在类初始化时,直接定义多线程执行(不同维度)的对象。
JUnitCore类中的方法runClasses():- public static Result runClasses(Computer computer,Class<?>... classes)
复制代码 可以在main()函数里直接运行测试用例,参数Computer是ParallelComputer的父类,可以直接new ParallelComputer(boolean classes, boolean methods)对象作为第一个形参。
实例1:
- public class A {
- @Test
- public void a() {
- assertThat(3, is(1));
- }
- @Test
- public void b() {
- assertThat(3, not(1));
- }
- }
- public class B {
- @Test
- public void c() {
- assertThat(3, greaterThan(1));
- }
- @Test
- public void d() {
- assertThat(3, lessThan(1));
- }
- }
复制代码- public class ParallelTest {
- public static void main(String[] args) {
- Class[] cls = { A.class, B.class };
- Result rt;
- // 并发以类为维度
- // rt = JUnitCore.runClasses(ParallelComputer.classes(), cls);
- // 并发以方法为维度
- // rt = JUnitCore.runClasses(ParallelComputer.methods(), cls);
- // 并发以类和方法为维度
- rt = JUnitCore.runClasses(new ParallelComputer(true, true), cls);
- System.out.println(rt.getRunCount() + " " + rt.getFailures() + " " + rt.getRunTime());
- }
- }
复制代码 // A,B两个类并发执行,但类的方法还是串行执行;
JUnitCore.runClasses(ParallelComputer.classes(), cls);
// A,B两个类串行执行,但类的方法并发执行
JUnitCore.runClasses(ParallelComputer.methods(), cls);
// A,B两个类并发执行,其方法也并发执行
JUnitCore.runClasses(new ParallelComputer(true, true), cls);
(二)自定义对单个类进行多线程执行的Runner- package com.weibo.concurrent;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.junit.runner.notification.RunNotifier;
- import org.junit.runners.BlockJUnit4ClassRunner;
- import org.junit.runners.model.FrameworkMethod;
- import org.junit.runners.model.InitializationError;
- import org.junit.runners.model.Statement;
- /**
- * Runs all tests in parallel and waits for them to complete.
- *
- */
- public class MultiThreadedRunner extends BlockJUnit4ClassRunner {
- private AtomicInteger numThreads;
- public static int maxThreads = 10;
- public MultiThreadedRunner (Class<?> klass) throws InitializationError {
- super (klass);
- numThreads = new AtomicInteger(0);
- }
- // Runs the test corresponding to child,which can be assumed to be an element of the list returned by getChildren()
- @Override
- protected void runChild(final FrameworkMethod method, final RunNotifier notifier) {
- while (numThreads.get() > maxThreads) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- System.err.println ("Interrupted: " + method.getName());
- e.printStackTrace();
- return; // The user may have interrupted us; this won't happen normally
- }
- }
- numThreads.incrementAndGet();
- // 用线程执行父类runChild(method, notifier)
- new Thread (new Test(method, notifier)).start();
- }
- // childrenInvoker() call runChild(Object, RunNotifier) on each object returned by getChildren()
- // evaluate() run the action, 调用父类BlockJUnit4ClassRunner的evaluate()
- @Override
- protected Statement childrenInvoker(final RunNotifier notifier) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- MultiThreadedRunner.super.childrenInvoker(notifier).evaluate();
- // wait for all child threads (tests) to complete
- while (numThreads.get() > 0) {
- Thread.sleep(1000);
- }
- }
- };
- }
- class Test implements Runnable {
- private final FrameworkMethod method;
- private final RunNotifier notifier;
- public Test (final FrameworkMethod method, final RunNotifier notifier) {
- this.method = method;
- this.notifier = notifier;
- }
- @Override
- public void run () {
- System.err.println (method.getName());
- MultiThreadedRunner.super.runChild(method, notifier);
- numThreads.decrementAndGet();
- }
- }
- }
复制代码
由于字数限制:请转至
JUnit4 多线程执行测试用例 (下)
|
|