首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

真正意义下的spring环境中的单元测试方案spring-test与mokito完美结合

2012-09-07 
真正意义上的spring环境中的单元测试方案spring-test与mokito完美结合一.要解决的问题:spring环境中单元测

真正意义上的spring环境中的单元测试方案spring-test与mokito完美结合
一.要解决的问题:
    spring环境中单元测试其实不是真正意义上的单元测试,真正意义上的单元测试应该是隔离所有的依赖,对当前业务实现业务逻辑测试;但是目前spring好像还没提供这样的解决方案,只能做依赖于环境的集成测试。比如:要测试A类,但是A类依赖B类和C类,这个时候我们必须保证B和C是完整的且是相对稳定的没太多bug的类.但是实际开发过程中,C类和B类可能是对数据库操作的Dao层或是对外接口层,这个时候我们在测试A类的时候业务B和C的环境或B或C都现在还没开发完成只是一个接口定义完成,这个时候就很难完成我们A类的测试了。



二.解决方案:
   为了解决这个问题我们必须在测试的时候忽略B和C类,换句话说就是假象B和C都是可以运行或按我们预期返回结果的运行。我们利用mockito来掩饰我们测试类的所有的依赖。这样我们需要做到两点1.我们可以让B和C可以控制返回预期;2.B和C必须注入到spring中替换我们的测试类的依赖.






DependencyInjectionAsMockitoTestExecutionListener类是在spring-test中的DependencyInjectionTestExecutionListener基础上扩展的一个结合mock的测试监听器。我们在测试的时候可以用注解TestExecutionListeners指定这个监听器来实现单元测试
package org.springframework.test.context.support;import java.lang.annotation.Annotation;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;import static org.mockito.Mockito.mock;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.TestContext;/** * @author yanglin lv */public class DependencyInjectionAsMockitoTestExecutionListener extends DependencyInjectionTestExecutionListener {    private static String SETTER = "set";    private static String GETTER = "get";    @Override    protected void injectDependencies(final TestContext testContext) throws Exception {        super.injectDependencies(testContext);        Object bean = testContext.getTestInstance();        Class[] mockClass = getMockClass(bean.getClass());        Method[] methods = bean.getClass().getDeclaredMethods();        Class clz = bean.getClass();        Object instance = null;        List<MockObjectMap> objs = new ArrayList<MockObjectMap>();        autowireMockBean(clz, bean, objs);        List<Object> stubObjs = getStubInstance(clz, bean);        autowireMockBeanForSpring(stubObjs, objs);    }    private void autowireMockBeanForSpring(List<Object> stubObjs, List<MockObjectMap> objs)                                                                                           throws IllegalArgumentException,                                                                                           IllegalAccessException,                                                                                           InvocationTargetException {        for (Object object : stubObjs) {            Class claz = object.getClass();            do {                for (Method method : claz.getDeclaredMethods()) {                    if (method.getName().startsWith(SETTER)) {                        for (MockObjectMap mockObjectMap : objs) {                            Object obj = method.getGenericParameterTypes()[0];                            if (obj instanceof java.lang.reflect.Type                                && mockObjectMap.getType().getName().equalsIgnoreCase(((Class) obj).getName())) {                                method.invoke(object, mockObjectMap.getObj());                                continue;                            }                        }                    }                }                claz = claz.getSuperclass();            } while (!claz.equals(Object.class));        }    }    private void autowireMockBean(Class clz, Object bean, List<MockObjectMap> objs) throws IllegalArgumentException,                                                                                   IllegalAccessException {        for (Field field : clz.getFields()) {            Annotation[] mockAnnotations = field.getAnnotations();            for (Annotation annotation : mockAnnotations) {                if (annotation instanceof org.mockito.Mock) {                    MockObjectMap mockObjectMap = new MockObjectMap();                    objs.add(mockObjectMap);                    mockObjectMap.setType(field.getType());                    mockObjectMap.setObj(mock(field.getType()));                    field.setAccessible(true);                    field.set(bean, mockObjectMap.getObj());                    continue;                }            }        }    }    /**     * 取得测试类中所有的mock对象的类型     *      * @param clazz     * @return     */    private Class[] getMockClass(Class claz) {        List<Class> clasList = new ArrayList<Class>();        Field[] fields = claz.getDeclaredFields();        for (Field field : fields) {            Annotation[] mockAnnotations = field.getAnnotations();            for (Annotation annotation : mockAnnotations) {                if (annotation instanceof org.mockito.Mock) {                    clasList.add(field.getType());                    continue;                }            }        }        return clasList.toArray(new Class[0]);    }    /**     * 取得测试类中测试桩类     *      * @param clazz     * @return     * @throws InvocationTargetException     * @throws IllegalAccessException     * @throws IllegalArgumentException     */    private List<Object> getStubInstance(Class clazz, Object bean) throws IllegalArgumentException,                                                                  IllegalAccessException, InvocationTargetException {        List<Object> objList = new ArrayList<Object>();        Field[] fields = clazz.getDeclaredFields();// 测试类中所有的域名        Method[] methods = clazz.getDeclaredMethods();        for (Field field : fields) {            Annotation[] mockAnnotations = field.getAnnotations();            for (Annotation annotation : mockAnnotations) {                if (annotation instanceof Autowired) {                    for (Method method : methods) {                        String name = field.getName();                        if (method.getName().startsWith(GETTER) && method.getName().substring(3).equalsIgnoreCase(name)) {                            objList.add(method.invoke(bean, null)); // 将所有的测试桩类放在objList                        }                    }                }            }        }        return objList;    }    private class MockObjectMap {        private Object   obj;        private Class<?> type;        public Object getObj() {            return obj;        }        public void setObj(Object obj) {            this.obj = obj;        }        public Class<?> getType() {            return type;        }        public void setType(Class<?> type) {            this.type = type;        }    }}

总结:这种方式可以真正的用spring来实现TDD面向接口的测试方案,对依赖的类做到完全屏蔽,对目前测试类和mock类设置期望输出简单实现

热点排行