zoukankan      html  css  js  c++  java
  • Junit 源码剖析(二)

    junit4 下的所有的testcase都是在Runner下执行的, 可以将Runner理解为junit运行的容器, 默认情况下junit会使用JUnit4ClassRunner作为所有testcase的执行容器。

    如果要定制自己的junit, 则可以实现自己的Runner,最简单的办法就是Junit4ClassRunner继承, spring-test, unitils这些框架就是采用这样的做法。

    如在spring中是SpringJUnit4ClassRunner, 在unitils中是UnitilsJUnit4TestClassRunner, 一般我们的testcase都是在通过eclipse插件来执行的, eclipse的junit插件会在执行的时候会初始化指定的Runner。初始化的过程可以在ClassRequest中找到。

    org.junit.internal.runners.Junit4ClassRunner

    package org.junit.internal.runners;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.Iterator;
    import java.util.List;
    
    import org.junit.runner.Description;
    import org.junit.runner.Runner;
    import org.junit.runner.manipulation.Filter;
    import org.junit.runner.manipulation.Filterable;
    import org.junit.runner.manipulation.NoTestsRemainException;
    import org.junit.runner.manipulation.Sortable;
    import org.junit.runner.manipulation.Sorter;
    import org.junit.runner.notification.Failure;
    import org.junit.runner.notification.RunNotifier;
    import org.junit.runners.BlockJUnit4ClassRunner;
    
    /**
     * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
     *             removed in the next release. Please use
     *             {@link BlockJUnit4ClassRunner} in place of
     *             {@link JUnit4ClassRunner}.
     * 
     *             This may disappear as soon as 1 April 2009
     */
    @Deprecated
    public class JUnit4ClassRunner extends Runner implements Filterable, Sortable
    {
        private final List<Method> fTestMethods;
    
        private TestClass fTestClass;
    	
        //将要测试的TestCase实例Class对象传入
        public JUnit4ClassRunner(Class<?> klass) throws InitializationError
        {
            fTestClass = new TestClass(klass);
            fTestMethods = getTestMethods();
            //对要进行测试的方法展开验证
            validate();
        }
        
        //获取@test的方法List
        protected List<Method> getTestMethods()
        {
            return fTestClass.getTestMethods();
        }
    	
        //对要进行测试的方法展开验证
        protected void validate() throws InitializationError
        {
            MethodValidator methodValidator = new MethodValidator(fTestClass);
            methodValidator.validateMethodsForDefaultRunner();
            methodValidator.assertValid();
        }
    
        @Override
        public void run(final RunNotifier notifier)
        {
            new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable()
            {
                public void run()
                {
                    runMethods(notifier);
                }
            }).runProtected();
        }
    
        protected void runMethods(final RunNotifier notifier)
        {
            for (Method method : fTestMethods)
                invokeTestMethod(method, notifier);
        }
    
        @Override
        public Description getDescription()
        {
            Description spec = Description.createSuiteDescription(getName(), classAnnotations());
            List<Method> testMethods = fTestMethods;
            for (Method method : testMethods)
                spec.addChild(methodDescription(method));
            return spec;
        }
    
        protected Annotation[] classAnnotations()
        {
            return fTestClass.getJavaClass().getAnnotations();
        }
    
        protected String getName()
        {
            return getTestClass().getName();
        }
    
        protected Object createTest() throws Exception
        {
            return getTestClass().getConstructor().newInstance();
        }
    
        protected void invokeTestMethod(Method method, RunNotifier notifier)
        {
            Description description = methodDescription(method);
            Object test;
            try
            {
                test = createTest();
            }
            catch(InvocationTargetException e)
            {
                testAborted(notifier, description, e.getCause());
                return;
            }
            catch(Exception e)
            {
                testAborted(notifier, description, e);
                return;
            }
            TestMethod testMethod = wrapMethod(method);
            new MethodRoadie(test, testMethod, notifier, description).run();
        }
    
        private void testAborted(RunNotifier notifier, Description description, Throwable e)
        {
            notifier.fireTestStarted(description);
            notifier.fireTestFailure(new Failure(description, e));
            notifier.fireTestFinished(description);
        }
    
        protected TestMethod wrapMethod(Method method)
        {
            return new TestMethod(method, fTestClass);
        }
    
        protected String testName(Method method)
        {
            return method.getName();
        }
    
        protected Description methodDescription(Method method)
        {
            return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method));
        }
    
        protected Annotation[] testAnnotations(Method method)
        {
            return method.getAnnotations();
        }
    
        public void filter(Filter filter) throws NoTestsRemainException
        {
            for (Iterator<Method> iter = fTestMethods.iterator(); iter.hasNext();)
            {
                Method method = iter.next();
                if (!filter.shouldRun(methodDescription(method)))
                    iter.remove();
            }
            if (fTestMethods.isEmpty())
                throw new NoTestsRemainException();
        }
    
        public void sort(final Sorter sorter)
        {
            Collections.sort(fTestMethods, new Comparator<Method>()
            {
                public int compare(Method o1, Method o2)
                {
                    return sorter.compare(methodDescription(o1), methodDescription(o2));
                }
            });
        }
    
        protected TestClass getTestClass()
        {
            return fTestClass;
        }
    }
    

      

    org.junit.internal.runners.TestClass类

    不同于上一节提到的org.junit.runners.model.TestClass类

    package org.junit.internal.runners;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    import org.junit.AfterClass;
    import org.junit.Before;
    import org.junit.BeforeClass;
    import org.junit.Test;
    import org.junit.runners.BlockJUnit4ClassRunner;
    
    /**
     * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
     *             removed in the next release. Please use
     *             {@link BlockJUnit4ClassRunner} in place of
     *             {@link JUnit4ClassRunner}.
     */
    @Deprecated
    public class TestClass
    {
        private final Class<?> fClass;
    
        public TestClass(Class<?> klass)
        {
            fClass = klass;
        }
    
        public List<Method> getTestMethods()
        {
            return getAnnotatedMethods(Test.class);
        }
    
        List<Method> getBefores()
        {
            return getAnnotatedMethods(BeforeClass.class);
        }
    
        List<Method> getAfters()
        {
            return getAnnotatedMethods(AfterClass.class);
        }
    
        public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass)
        {
            List<Method> results = new ArrayList<Method>();
            for (Class<?> eachClass : getSuperClasses(fClass))
            {
                Method[] methods = eachClass.getDeclaredMethods();
                for (Method eachMethod : methods)
                {
                    Annotation annotation = eachMethod.getAnnotation(annotationClass);
                    if (annotation != null && !isShadowed(eachMethod, results))
                        results.add(eachMethod);
                }
            }
            if (runsTopToBottom(annotationClass))
                Collections.reverse(results);
            return results;
        }
    
        private boolean runsTopToBottom(Class<? extends Annotation> annotation)
        {
            return annotation.equals(Before.class) || annotation.equals(BeforeClass.class);
        }
    
        private boolean isShadowed(Method method, List<Method> results)
        {
            for (Method each : results)
            {
                if (isShadowed(method, each))
                    return true;
            }
            return false;
        }
    
        private boolean isShadowed(Method current, Method previous)
        {
            if (!previous.getName().equals(current.getName()))
                return false;
            if (previous.getParameterTypes().length != current.getParameterTypes().length)
                return false;
            for (int i = 0; i < previous.getParameterTypes().length; i++)
            {
                if (!previous.getParameterTypes()[i].equals(current.getParameterTypes()[i]))
                    return false;
            }
            return true;
        }
    
        private List<Class<?>> getSuperClasses(Class<?> testClass)
        {
            ArrayList<Class<?>> results = new ArrayList<Class<?>>();
            Class<?> current = testClass;
            while (current != null)
            {
                results.add(current);
                current = current.getSuperclass();
            }
            return results;
        }
    
        public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException
        {
            return fClass.getConstructor();
        }
    
        public Class<?> getJavaClass()
        {
            return fClass;
        }
    
        public String getName()
        {
            return fClass.getName();
        }
    
    }
    

      

    org.junit.internal.runners.MethodValidator类

    用于在org.junit.internal.runners.Junit4ClassRunner 类当中进行方法验证

    package org.junit.internal.runners;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.junit.After;
    import org.junit.AfterClass;
    import org.junit.Before;
    import org.junit.BeforeClass;
    import org.junit.Test;
    import org.junit.runners.BlockJUnit4ClassRunner;
    
    /**
     * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
     *             removed in the next release. Please use
     *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
     */
    @Deprecated
    public class MethodValidator {
    
    	private final List<Throwable> fErrors= new ArrayList<Throwable>();
    
    	private TestClass fTestClass;
        
    	//org.junit.internal.runners.JUnit4ClassRunner类中
    	//validate()方法中调用该构造函数-----第一步
    	public MethodValidator(TestClass testClass) {
    		fTestClass = testClass;
    	}
    
    	public void validateInstanceMethods() {
    		validateTestMethods(After.class, false);
    		validateTestMethods(Before.class, false);
    		validateTestMethods(Test.class, false);
    		
    		List<Method> methods= fTestClass.getAnnotatedMethods(Test.class);
    		if (methods.size() == 0)
    			fErrors.add(new Exception("No runnable methods"));
    	}
    
    	public void validateStaticMethods() {
    		validateTestMethods(BeforeClass.class, true);
    		validateTestMethods(AfterClass.class, true);
    	}
    	
    	//Junit4ClassRunner类中调用第二步
    	public List<Throwable> validateMethodsForDefaultRunner() {
    		//校验无参构造方法
    		validateNoArgConstructor();
    		//校验注解方法
    		validateStaticMethods();
    		validateInstanceMethods();
    		return fErrors;
    	}
    	
    	//Junit4ClassRunner类中第三步
    	public void assertValid() throws InitializationError {
    		if (!fErrors.isEmpty())
    			throw new InitializationError(fErrors);
    	}
    
    	public void validateNoArgConstructor() {
    		try {
    			fTestClass.getConstructor();
    		} catch (Exception e) {
    			fErrors.add(new Exception("Test class should have public zero-argument constructor", e));
    		}
    	}
    	
        //校验要测试的方法 是否静态方法、是否public方法、是否返回值void、是否无参数
    	//@param annotation 要测试的annotation类  
    	//@param isStatic 是否要求静态方法
    	private void validateTestMethods(Class<? extends Annotation> annotation,boolean isStatic) {
    		List<Method> methods= fTestClass.getAnnotatedMethods(annotation);
    		
    		for (Method each : methods) {
    			if (Modifier.isStatic(each.getModifiers()) != isStatic) {
    				String state= isStatic ? "should" : "should not";
    				fErrors.add(new Exception("Method " + each.getName() + "() "
    						+ state + " be static"));
    			}
    			if (!Modifier.isPublic(each.getDeclaringClass().getModifiers()))
    				fErrors.add(new Exception("Class " + each.getDeclaringClass().getName()
    						+ " should be public"));
    			if (!Modifier.isPublic(each.getModifiers()))
    				fErrors.add(new Exception("Method " + each.getName()
    						+ " should be public"));
    			if (each.getReturnType() != Void.TYPE)
    				fErrors.add(new Exception("Method " + each.getName()
    						+ " should be void"));
    			if (each.getParameterTypes().length != 0)
    				fErrors.add(new Exception("Method " + each.getName()
    						+ " should have no parameters"));
    		}
    	}
    }
    

      

      

  • 相关阅读:
    洛谷 P1604 B进制星球
    629D
    。。。。。
    新安排
    计算几何的两道题
    cf C题
    昨天
    最近几天的安排
    最近几天
    杂论——持续更新。
  • 原文地址:https://www.cnblogs.com/wuxinliulei/p/4933851.html
Copyright © 2011-2022 走看看