代码改变世界

理一理Spring如何对接JUnit

2017-11-04 21:34  chen.simon  阅读(293)  评论(0编辑  收藏  举报

测试代码

package org.simonme.srcstudy.spring3.demo.stub;

import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.simonme.srcstudy.spring3.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * <一句话功能简述>
 * <功能详细描述>
 * 
 * @author  http://www.cnblogs.com/simoncook
 * @version  [版本号, 2017年11月4日]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"})
public class UserServiceAssemblyByJUnit
{
    
    
    private UserService userService;

    @Test
    public void test()
    {
        assertNotNull(userService);
    }

    public UserService getUserService()
    {
        return userService;
    }

    @Autowired
    public void setUserService(UserService userService)
    {
        this.userService = userService;
    }

分析方式

this.userService = userService; 这一行直接断点

堆栈信息

org.simonme.srcstudy.spring3.demo.stub.UserServiceAssemblyByJUnit.setUserService(org.simonme.srcstudy.spring3.demo.service.UserService) line: 50	
sun.reflect.NativeMethodAccessorImpl.invoke0(java.lang.reflect.Method, java.lang.Object, java.lang.Object[]) line: not available [native method]	
sun.reflect.NativeMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) line: 39	
sun.reflect.DelegatingMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) line: 25	
java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object...) line: 597	
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(java.lang.Object, java.lang.String, org.springframework.beans.PropertyValues) line: 582	
org.springframework.beans.factory.annotation.InjectionMetadata.inject(java.lang.Object, java.lang.String, org.springframework.beans.PropertyValues) line: 84	
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(org.springframework.beans.PropertyValues, java.beans.PropertyDescriptor[], java.lang.Object, java.lang.String) line: 282	
org.springframework.beans.factory.support.DefaultListableBeanFactory(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory).populateBean(java.lang.String, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.BeanWrapper) line: 1074	
org.springframework.beans.factory.support.DefaultListableBeanFactory(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory).autowireBeanProperties(java.lang.Object, int, boolean) line: 374	
org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(org.springframework.test.context.TestContext) line: 110	
org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(org.springframework.test.context.TestContext) line: 75	
org.springframework.test.context.TestContextManager.prepareTestInstance(java.lang.Object) line: 321	
org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest() line: 220	
org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall() line: 301	
org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1(org.junit.internal.runners.model.ReflectiveCallable).run() line: 12	
org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(org.junit.runners.model.FrameworkMethod) line: 303	
org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(org.junit.runners.model.FrameworkMethod, org.junit.runner.notification.RunNotifier) line: 240	
org.springframework.test.context.junit4.SpringJUnit4ClassRunner(org.junit.runners.BlockJUnit4ClassRunner).runChild(java.lang.Object, org.junit.runner.notification.RunNotifier) line: 50	
org.junit.runners.ParentRunner$3.run() line: 238	
org.junit.runners.ParentRunner$1.schedule(java.lang.Runnable) line: 63	
org.springframework.test.context.junit4.SpringJUnit4ClassRunner(org.junit.runners.ParentRunner<T>).runChildren(org.junit.runner.notification.RunNotifier) line: 236	
org.junit.runners.ParentRunner<T>.access$000(org.junit.runners.ParentRunner, org.junit.runner.notification.RunNotifier) line: 53	
org.junit.runners.ParentRunner$2.evaluate() line: 229	

看junit的运作方式

从main方法到runner

翻看官方guide 很容易发现JUnitCore是main方法所在类
JUnitCore是如何到runner的呢? 看下面分析

org.junit.runner.JUnitCore.main(String...)
// 一些listener构造之类,然后通过AllDefaultPossibilitiesBuilder 构建runner  
org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(Class<?>)
List<RunnerBuilder> builders = Arrays.asList(
                ignoredBuilder(),
                annotatedBuilder(),
                suiteMethodBuilder(),
                junit3Builder(),
                junit4Builder());

        for (RunnerBuilder each : builders) {
            Runner runner = each.safeRunnerForClass(testClass);
            if (runner != null) {
                return runner;
            }
        }

找到一个就直接返回
ignoredBuilder 可以用过Ignore注解忽略你的test case
annotatedBuilder 是找 RunWith注解定义的自定义runner
junit3Builder 如果test case 继承自TestCase类 则用junit3的runner
junit4Builder 直接对接 BlockJUnit4ClassRunner
spring的SpringJUnit4ClassRunner也是继承自BlockJUnit4ClassRunner

回头去看上面的堆栈信息 一目了然
关键点在于重写 org.junit.runners.BlockJUnit4ClassRunner.createTest() 这个方法

protected Object createTest() throws Exception {
        return getTestClass().getOnlyConstructor().newInstance();
    }
/**
	 * Delegates to the parent implementation for creating the test instance and
	 * then allows the {@link #getTestContextManager() TestContextManager} to
	 * prepare the test instance before returning it.
	 * 
	 * @see TestContextManager#prepareTestInstance(Object)
	 */
	@Override
	protected Object createTest() throws Exception {
		Object testInstance = super.createTest();
		getTestContextManager().prepareTestInstance(testInstance);
		return testInstance;
	}

也就是把JUnit原生的创建test case的instance的过程接用spring容器装配的方式接管过来就可以了。