surefire 拉起 junit 单元测试类 源码阅读(二)

本文详细解析了JUnit测试执行的具体流程,从ForkedBooter的启动到BlockJUnit4ClassRunner运行子类的过程,深入探讨了不同JUnit版本间的调用差异,并通过具体的类代码示例解释了关键步骤。

根据上一篇blog,看到日志1流程:

surefire.ForkedBooter -> ForkedBooter.runSuitesInProcess

-> ForkedBooter.invokeProviderInSameClassLoader
-> surefire.junit4.JUnit4Provider.invoke -> executeTestSet -> executeWithRerun
-> execute
-> junit.runners.ParentRunner.run -> evaluate -> runChildren -> schedule -> run
-> BlockJUnit4ClassRunner.runChild
-> junit.runners.ParentRunner.runLeaf
-> junit.rules.RunRules.evaluate
-> org.junit.internal.runners.statements.RunBefores.evaluate
-> junit.runners.model.FrameworkMethod.invokeExplosively
-> junit.internal.runners.model.ReflectiveCallable.run

-> junit.runners.model.FrameworkMethod.runReflectiveCall

这是 一个junit的版本调用过程,不同junit 版本,调用类不同,如日志2

-> ForkedBooter.invokeProviderInSameClassLoader
-> surefire.junit4.JUnit4Provider.invoke -> executeTestSet -> executeWithRerun
-> execute
-> junit.runners.ParentRunner.run -> evaluate -> runChildren -> schedule -> run

-> BlockJUnit4ClassRunner.runChild

-> org.junit.internal.runners.statements.InvokeMethod.evaluate

-> org.junit.runners.model.FrameworkMethod.invokeExplosively

->  org.junit.internal.runners.model.ReflectiveCallable.run
->  org.junit.runners.model.FrameworkMethod$1.runReflectiveCall

InvokeMethod 了类代码:

public class InvokeMethod extends Statement {
	private final FrameworkMethod fTestMethod;
	private Object fTarget;
	
	public InvokeMethod(FrameworkMethod testMethod, Object target) {
		fTestMethod= testMethod;
		fTarget= target;
	}
	
	@Override
	public void evaluate() throws Throwable {
		fTestMethod.invokeExplosively(fTarget);
	}

Statement:

public abstract class Statement {
	/**
	 * Run the action, throwing a {@code Throwable} if anything goes wrong.
	 */
	public abstract void evaluate() throws Throwable;
}

FrameworkMethod代码:

public class FrameworkMethod {
	private final Method fMethod;

	/**
	 * Returns a new {@code FrameworkMethod} for {@code method}
	 */
	public FrameworkMethod(Method method) {
		fMethod= method;
	}

	/**
	 * Returns the underlying Java method
	 */
	public Method getMethod() {
		return fMethod;
	}

	/**
	 * Returns the result of invoking this method on {@code target} with
	 * parameters {@code params}. {@link InvocationTargetException}s thrown are
	 * unwrapped, and their causes rethrown.
	 */
	public Object invokeExplosively(final Object target, final Object... params)
			throws Throwable {
		return new ReflectiveCallable() {
			@Override
			protected Object runReflectiveCall() throws Throwable {
				return fMethod.invoke(target, params);
			}
		}.run();
	}

	/**
	 * Returns the method's name
	 */
	public String getName() {
		return fMethod.getName();
	}

	/**
	 * Adds to {@code errors} if this method:
	 * <ul>
	 * <li>is not public, or
	 * <li>takes parameters, or
	 * <li>returns something other than void, or
	 * <li>is static (given {@code isStatic is false}), or
	 * <li>is not static (given {@code isStatic is true}).
	 */
	public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
		validatePublicVoid(isStatic, errors);
		if (fMethod.getParameterTypes().length != 0)
			errors.add(new Exception("Method " + fMethod.getName() + " should have no parameters"));
	}


	/**
	 * Adds to {@code errors} if this method:
	 * <ul>
	 * <li>is not public, or
	 * <li>returns something other than void, or
	 * <li>is static (given {@code isStatic is false}), or
	 * <li>is not static (given {@code isStatic is true}).
	 */
	public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
		if (Modifier.isStatic(fMethod.getModifiers()) != isStatic) {
			String state= isStatic ? "should" : "should not";
			errors.add(new Exception("Method " + fMethod.getName() + "() " + state + " be static"));
		}
		if (!Modifier.isPublic(fMethod.getDeclaringClass().getModifiers()))
			errors.add(new Exception("Class " + fMethod.getDeclaringClass().getName() + " should be public"));
		if (!Modifier.isPublic(fMethod.getModifiers()))
			errors.add(new Exception("Method " + fMethod.getName() + "() should be public"));
		if (fMethod.getReturnType() != Void.TYPE)
			errors.add(new Exception("Method " + fMethod.getName() + "() should be void"));
	}

	boolean isShadowedBy(List<FrameworkMethod> results) {
		for (FrameworkMethod each : results)
			if (isShadowedBy(each))
				return true;
		return false;
	}

	private boolean isShadowedBy(FrameworkMethod each) {
		if (!each.getName().equals(getName()))
			return false;
		if (each.getParameterTypes().length != getParameterTypes().length)
			return false;
		for (int i= 0; i < each.getParameterTypes().length; i++)
			if (!each.getParameterTypes()[i].equals(getParameterTypes()[i]))
				return false;
		return true;
	}

	@Override
	public boolean equals(Object obj) {
		if (!FrameworkMethod.class.isInstance(obj))
			return false;
		return ((FrameworkMethod) obj).fMethod.equals(fMethod);
	}

	@Override
	public int hashCode() {
		return fMethod.hashCode();
	}

	/**
	 * Returns true iff this is a no-arg method that returns a value assignable
	 * to {@code type}
	 */
	public boolean producesType(Class<?> type) {
		return getParameterTypes().length == 0
				&& type.isAssignableFrom(fMethod.getReturnType());
	}

	private Class<?>[] getParameterTypes() {
		return fMethod.getParameterTypes();
	}

	/**
	 * Returns the annotations on this method
	 */
	public Annotation[] getAnnotations() {
		return fMethod.getAnnotations();
	}

	/**
	 * Returns the annotation of type {@code annotationType} on this method, if
	 * one exists.
	 */
	public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
		return fMethod.getAnnotation(annotationType);
	}
}
ReflectiveCallable类:
public abstract class ReflectiveCallable {
	public Object run() throws Throwable {
		try {
			return runReflectiveCall();
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		}
	}

	protected abstract Object runReflectiveCall() throws Throwable;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值