zoukankan      html  css  js  c++  java
  • 真正意义上的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中替换我们的测试类的依赖.





    Java代码 复制代码 收藏代码
    1. /** 
    2.  * 类SayServiceTests.java的实现描述:Mock demo 
    3.  *  
    4.  * @author hujf 2011-3-2 下午02:04:08 
    5.  */  
    6. @ContextConfiguration(locations = { "/applicationContext.xml" })  
    7. @RunWith(SpringJUnit4ClassRunner.class)  
    8. @TestExecutionListeners({ org.springframework.test.context.support.DependencyInjectionAsMockitoTestExecutionListener.class })  
    9. public class SayServiceTest {  
    10.   
    11.     @Mock  
    12.     public SayDao     sayDao;  
    13.   
    14.     @Autowired  
    15.     public SayService sayService; // TODO 暂时用实现类  
    16.   
    17.     @Test  
    18.     public void testSay() {  
    19.         // 1.设置预期行为 void  
    20.   
    21.         when(sayDao.sayTo(null)).thenReturn("3");  
    22.         // 2.验证  
    23.         assertTrue(sayService.sayTo(null).equals("3"));  
    24.   
    25.     }  
    26.   
    27.     public SayDao getSayDao() {  
    28.         return sayDao;  
    29.     }  
    30.   
    31.     public void setSayDao(SayDao sayDao) {  
    32.         this.sayDao = sayDao;  
    33.     }  
    34.   
    35.     public SayService getSayService() {  
    36.         return sayService;  
    37.     }  
    38.   
    39.     public void setSayService(SayService sayService) {  
    40.         this.sayService = sayService;  
    41.     }  
    42.   
    43.     @Test  
    44.     public void testSayTo() {  
    45.         System.out.println("testSayTo...");  
    46.   
    47.         // 1.设置预期行为  
    48.         when(sayDao.sayTo(any(Person.class))).thenAnswer(new Answer() {  
    49.   
    50.             @Override  
    51.             public Object answer(InvocationOnMock invocation) throws Throwable {  
    52.                 Object[] args = invocation.getArguments();  
    53.                 // SayDao mock = (SayDao)invocation.getMock(); //Object mock = invocation.getMock();  
    54.   
    55.                 if (null != args && args.length > 0) {  
    56.                     Person person = (Person) args[0];  
    57.                     return person.getName();  
    58.                 }  
    59.                 return null;  
    60.             }  
    61.   
    62.         });  
    63.   
    64.         // 2.验证  
    65.         Person person = new Person();  
    66.         person.setId(11);  
    67.         person.setName("Leifeng");  
    68.   
    69.         String s = sayService.sayTo(person);  
    70.         System.out.println(s);  
    71.   
    72.         assertSame("Leifeng", s);  
    73.     }  
    74.   
    75.     @Test  
    76.     public void testSaySomething() {  
    77.         System.out.println("testSaySomething...");  
    78.         // 1.设置预期行为  
    79.         when(sayDao.saySomething(anyString(), any(Person.class), anyString())).thenAnswer(new Answer<String>() {  
    80.   
    81.             @Override  
    82.             public String answer(InvocationOnMock invocation) throws Throwable {  
    83.                 Object[] args = invocation.getArguments();  
    84.                 if (null != args && args.length > 0) {  
    85.                     String hello = (String) args[0];  
    86.                     Person person = (Person) args[1];  
    87.                     String msg = (String) args[2];  
    88.   
    89.                     return hello + "," + person.getName() + "(" + person.getId() + ")" + ":" + msg;  
    90.                     // SayDao dao = new SayDaoImpl();  
    91.                     // return dao.saySomething(hello, person, msg);  
    92.                 }  
    93.   
    94.                 return null;  
    95.             }  
    96.   
    97.         });  
    98.   
    99.         // 2.验证  
    100.         Person person = new Person();  
    101.         person.setId(12);  
    102.         person.setName("Leifeng");  
    103.         String s = sayService.saySomething("Welcome", person, "handsome guy!");  
    104.         System.out.println(s);  
    105.         assertNotNull(s);  
    106.     }  
    107.   
    108.     @Test  
    109.     public void testQueryPerson() {  
    110.         // 1.预置预期行为  
    111.         List<Person> personList = new ArrayList<Person>();  
    112.         // 初始化List《Person》  
    113.         for (int i = 0; i < 10; i++) {  
    114.             Person person = new Person();  
    115.             person.setId(i + 1);  
    116.             person.setName("name" + i);  
    117.             personList.add(person);  
    118.         }  
    119.         when(sayDao.queryPerson(any(Person.class))).thenReturn(personList);  
    120.   
    121.         // 2.验证  
    122.         Person query = new Person();  
    123.         query.setId(13);  
    124.         query.setName("Leifeng");  
    125.   
    126.         List<Person> list = sayService.queryPerson(query);  
    127.         assertTrue(10 == list.size());  
    128.   
    129.         // 重要(根据具体业务设计)  
    130.         assertTrue(3 == list.get(3).getFlag());  
    131.     }  
    132. }  
    /**
    * 类SayServiceTests.java的实现描述:Mock demo
    *
    * @author hujf 2011-3-2 下午02:04:08
    */
    @ContextConfiguration(locations = { "/applicationContext.xml" })
    @RunWith(SpringJUnit4ClassRunner.class)
    @TestExecutionListeners({ org.springframework.test.context.support.DependencyInjectionAsMockitoTestExecutionListener.class })
    public class SayServiceTest {
     
        @Mock
        public SayDao     sayDao;
     
        @Autowired
        public SayService sayService; // TODO 暂时用实现类
     
        @Test
        public void testSay() {
            // 1.设置预期行为 void
     
            when(sayDao.sayTo(null)).thenReturn("3");
            // 2.验证
            assertTrue(sayService.sayTo(null).equals("3"));
     
        }
     
        public SayDao getSayDao() {
            return sayDao;
        }
     
        public void setSayDao(SayDao sayDao) {
            this.sayDao = sayDao;
        }
     
        public SayService getSayService() {
            return sayService;
        }
     
        public void setSayService(SayService sayService) {
            this.sayService = sayService;
        }
     
        @Test
        public void testSayTo() {
            System.out.println("testSayTo...");
     
            // 1.设置预期行为
            when(sayDao.sayTo(any(Person.class))).thenAnswer(new Answer() {
     
                @Override
                public Object answer(InvocationOnMock invocation) throws Throwable {
                    Object[] args = invocation.getArguments();
                    // SayDao mock = (SayDao)invocation.getMock(); //Object mock = invocation.getMock();
     
                    if (null != args && args.length > 0) {
                        Person person = (Person) args[0];
                        return person.getName();
                    }
                    return null;
                }
     
            });
     
            // 2.验证
            Person person = new Person();
            person.setId(11);
            person.setName("Leifeng");
     
            String s = sayService.sayTo(person);
            System.out.println(s);
     
            assertSame("Leifeng", s);
        }
     
        @Test
        public void testSaySomething() {
            System.out.println("testSaySomething...");
            // 1.设置预期行为
            when(sayDao.saySomething(anyString(), any(Person.class), anyString())).thenAnswer(new Answer<String>() {
     
                @Override
                public String answer(InvocationOnMock invocation) throws Throwable {
                    Object[] args = invocation.getArguments();
                    if (null != args && args.length > 0) {
                        String hello = (String) args[0];
                        Person person = (Person) args[1];
                        String msg = (String) args[2];
     
                        return hello + "," + person.getName() + "(" + person.getId() + ")" + ":" + msg;
                        // SayDao dao = new SayDaoImpl();
                        // return dao.saySomething(hello, person, msg);
                    }
     
                    return null;
                }
     
            });
     
            // 2.验证
            Person person = new Person();
            person.setId(12);
            person.setName("Leifeng");
            String s = sayService.saySomething("Welcome", person, "handsome guy!");
            System.out.println(s);
            assertNotNull(s);
        }
     
        @Test
        public void testQueryPerson() {
            // 1.预置预期行为
            List<Person> personList = new ArrayList<Person>();
            // 初始化List《Person》
            for (int i = 0; i < 10; i++) {
                Person person = new Person();
                person.setId(i + 1);
                person.setName("name" + i);
                personList.add(person);
            }
            when(sayDao.queryPerson(any(Person.class))).thenReturn(personList);
     
            // 2.验证
            Person query = new Person();
            query.setId(13);
            query.setName("Leifeng");
     
            List<Person> list = sayService.queryPerson(query);
            assertTrue(10 == list.size());
     
            // 重要(根据具体业务设计)
            assertTrue(3 == list.get(3).getFlag());
        }
    }

    DependencyInjectionAsMockitoTestExecutionListener类是在spring-test中的DependencyInjectionTestExecutionListener基础上扩展的一个结合mock的测试监听器。我们在测试的时候可以用注解TestExecutionListeners指定这个监听器来实现单元测试
    Java代码 复制代码 收藏代码
    1. package org.springframework.test.context.support;  
    2.   
    3. import java.lang.annotation.Annotation;  
    4. import java.lang.reflect.Field;  
    5. import java.lang.reflect.InvocationTargetException;  
    6. import java.lang.reflect.Method;  
    7.   
    8. import java.util.ArrayList;  
    9. import java.util.List;  
    10.   
    11. import static org.mockito.Mockito.mock;  
    12. import org.springframework.beans.factory.annotation.Autowired;  
    13. import org.springframework.test.context.TestContext;  
    14.   
    15. /** 
    16.  * @author yanglin lv 
    17.  */  
    18. public class DependencyInjectionAsMockitoTestExecutionListener extends DependencyInjectionTestExecutionListener {  
    19.   
    20.     private static String SETTER = "set";  
    21.   
    22.     private static String GETTER = "get";  
    23.   
    24.     @Override  
    25.     protected void injectDependencies(final TestContext testContext) throws Exception {  
    26.         super.injectDependencies(testContext);  
    27.         Object bean = testContext.getTestInstance();  
    28.         Class[] mockClass = getMockClass(bean.getClass());  
    29.         Method[] methods = bean.getClass().getDeclaredMethods();  
    30.         Class clz = bean.getClass();  
    31.         Object instance = null;  
    32.         List<MockObjectMap> objs = new ArrayList<MockObjectMap>();  
    33.         autowireMockBean(clz, bean, objs);  
    34.         List<Object> stubObjs = getStubInstance(clz, bean);  
    35.         autowireMockBeanForSpring(stubObjs, objs);  
    36.     }  
    37.   
    38.     private void autowireMockBeanForSpring(List<Object> stubObjs, List<MockObjectMap> objs)  
    39.                                                                                            throws IllegalArgumentException,  
    40.                                                                                            IllegalAccessException,  
    41.                                                                                            InvocationTargetException {  
    42.   
    43.         for (Object object : stubObjs) {  
    44.             Class claz = object.getClass();  
    45.             do {  
    46.                 for (Method method : claz.getDeclaredMethods()) {  
    47.                     if (method.getName().startsWith(SETTER)) {  
    48.                         for (MockObjectMap mockObjectMap : objs) {  
    49.                             Object obj = method.getGenericParameterTypes()[0];  
    50.                             if (obj instanceof java.lang.reflect.Type  
    51.                                 && mockObjectMap.getType().getName().equalsIgnoreCase(((Class) obj).getName())) {  
    52.                                 method.invoke(object, mockObjectMap.getObj());  
    53.                                 continue;  
    54.                             }  
    55.   
    56.                         }  
    57.   
    58.                     }  
    59.                 }  
    60.   
    61.                 claz = claz.getSuperclass();  
    62.             } while (!claz.equals(Object.class));  
    63.         }  
    64.   
    65.     }  
    66.   
    67.     private void autowireMockBean(Class clz, Object bean, List<MockObjectMap> objs) throws IllegalArgumentException,  
    68.                                                                                    IllegalAccessException {  
    69.   
    70.         for (Field field : clz.getFields()) {  
    71.   
    72.             Annotation[] mockAnnotations = field.getAnnotations();  
    73.             for (Annotation annotation : mockAnnotations) {  
    74.                 if (annotation instanceof org.mockito.Mock) {  
    75.                     MockObjectMap mockObjectMap = new MockObjectMap();  
    76.                     objs.add(mockObjectMap);  
    77.                     mockObjectMap.setType(field.getType());  
    78.                     mockObjectMap.setObj(mock(field.getType()));  
    79.                     field.setAccessible(true);  
    80.                     field.set(bean, mockObjectMap.getObj());  
    81.   
    82.                     continue;  
    83.                 }  
    84.   
    85.             }  
    86.   
    87.         }  
    88.   
    89.     }  
    90.   
    91.     /** 
    92.      * 取得测试类中所有的mock对象的类型 
    93.      *  
    94.      * @param clazz 
    95.      * @return 
    96.      */  
    97.     private Class[] getMockClass(Class claz) {  
    98.         List<Class> clasList = new ArrayList<Class>();  
    99.         Field[] fields = claz.getDeclaredFields();  
    100.         for (Field field : fields) {  
    101.             Annotation[] mockAnnotations = field.getAnnotations();  
    102.             for (Annotation annotation : mockAnnotations) {  
    103.                 if (annotation instanceof org.mockito.Mock) {  
    104.                     clasList.add(field.getType());  
    105.                     continue;  
    106.                 }  
    107.   
    108.             }  
    109.         }  
    110.         return clasList.toArray(new Class[0]);  
    111.     }  
    112.   
    113.     /** 
    114.      * 取得测试类中测试桩类 
    115.      *  
    116.      * @param clazz 
    117.      * @return 
    118.      * @throws InvocationTargetException 
    119.      * @throws IllegalAccessException 
    120.      * @throws IllegalArgumentException 
    121.      */  
    122.     private List<Object> getStubInstance(Class clazz, Object bean) throws IllegalArgumentException,  
    123.                                                                   IllegalAccessException, InvocationTargetException {  
    124.   
    125.         List<Object> objList = new ArrayList<Object>();  
    126.         Field[] fields = clazz.getDeclaredFields();// 测试类中所有的域名  
    127.         Method[] methods = clazz.getDeclaredMethods();  
    128.         for (Field field : fields) {  
    129.             Annotation[] mockAnnotations = field.getAnnotations();  
    130.             for (Annotation annotation : mockAnnotations) {  
    131.                 if (annotation instanceof Autowired) {  
    132.   
    133.                     for (Method method : methods) {  
    134.                         String name = field.getName();  
    135.                         if (method.getName().startsWith(GETTER) && method.getName().substring(3).equalsIgnoreCase(name)) {  
    136.                             objList.add(method.invoke(bean, null)); // 将所有的测试桩类放在objList  
    137.                         }  
    138.                     }  
    139.   
    140.                 }  
    141.   
    142.             }  
    143.         }  
    144.         return objList;  
    145.   
    146.     }  
    147.   
    148.     private class MockObjectMap {  
    149.   
    150.         private Object   obj;  
    151.   
    152.         private Class<?> type;  
    153.   
    154.         public Object getObj() {  
    155.             return obj;  
    156.         }  
    157.   
    158.         public void setObj(Object obj) {  
    159.             this.obj = obj;  
    160.         }  
    161.   
    162.         public Class<?> getType() {  
    163.             return type;  
    164.         }  
    165.   
    166.         public void setType(Class<?> type) {  
    167.             this.type = type;  
    168.         }  
    169.   
    170.     }  
    171.   
    172. }  
    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类设置期望输出简单实现

      




  • 相关阅读:
    Redis系列 (一) Ubuntu环境下搭建
    HIve高级函数
    SparkCore系列(三)广播变量和累加器
    SparkCore系列(二)rdd聚合操作,rdd之间聚合操作
    SparkCore系列(一)变换操作,查找取值操作
    从零学scala(九)类型参数、高级类型
    sparksql系列(六) SparkSql中UDF、UDAF、UDTF
    从零学scala(八)注解、XML处理
    从零学scala(七)集合、模式匹配和样例类
    Linux文件系统,硬链接、软链接、iNode、dentry
  • 原文地址:https://www.cnblogs.com/zDanica/p/5471652.html
Copyright © 2011-2022 走看看