首页 技术 正文
技术 2022年11月20日
0 收藏 323 点赞 3,463 浏览 16070 个字

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

单元测试SpringCC++C# 一.要解决的问题:
    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中替换我们的测试类的依赖.

  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. @Mock
  11. public SayDao     sayDao;
  12. @Autowired
  13. public SayService sayService; // TODO 暂时用实现类
  14. @Test
  15. public void testSay() {
  16. // 1.设置预期行为 void
  17. when(sayDao.sayTo(null)).thenReturn(“3”);
  18. // 2.验证
  19. assertTrue(sayService.sayTo(null).equals(“3”));
  20. }
  21. public SayDao getSayDao() {
  22. return sayDao;
  23. }
  24. public void setSayDao(SayDao sayDao) {
  25. this.sayDao = sayDao;
  26. }
  27. public SayService getSayService() {
  28. return sayService;
  29. }
  30. public void setSayService(SayService sayService) {
  31. this.sayService = sayService;
  32. }
  33. @Test
  34. public void testSayTo() {
  35. System.out.println(“testSayTo…”);
  36. // 1.设置预期行为
  37. when(sayDao.sayTo(any(Person.class))).thenAnswer(new Answer() {
  38. @Override
  39. public Object answer(InvocationOnMock invocation) throws Throwable {
  40. Object[] args = invocation.getArguments();
  41. // SayDao mock = (SayDao)invocation.getMock(); //Object mock = invocation.getMock();
  42. if (null != args && args.length > 0) {
  43. Person person = (Person) args[0];
  44. return person.getName();
  45. }
  46. return null;
  47. }
  48. });
  49. // 2.验证
  50. Person person = new Person();
  51. person.setId(11);
  52. person.setName(“Leifeng”);
  53. String s = sayService.sayTo(person);
  54. System.out.println(s);
  55. assertSame(“Leifeng”, s);
  56. }
  57. @Test
  58. public void testSaySomething() {
  59. System.out.println(“testSaySomething…”);
  60. // 1.设置预期行为
  61. when(sayDao.saySomething(anyString(), any(Person.class), anyString())).thenAnswer(new Answer<String>() {
  62. @Override
  63. public String answer(InvocationOnMock invocation) throws Throwable {
  64. Object[] args = invocation.getArguments();
  65. if (null != args && args.length > 0) {
  66. String hello = (String) args[0];
  67. Person person = (Person) args[1];
  68. String msg = (String) args[2];
  69. return hello + “,” + person.getName() + “(” + person.getId() + “)” + “:” + msg;
  70. // SayDao dao = new SayDaoImpl();
  71. // return dao.saySomething(hello, person, msg);
  72. }
  73. return null;
  74. }
  75. });
  76. // 2.验证
  77. Person person = new Person();
  78. person.setId(12);
  79. person.setName(“Leifeng”);
  80. String s = sayService.saySomething(“Welcome”, person, “handsome guy!”);
  81. System.out.println(s);
  82. assertNotNull(s);
  83. }
  84. @Test
  85. public void testQueryPerson() {
  86. // 1.预置预期行为
  87. List<Person> personList = new ArrayList<Person>();
  88. // 初始化List《Person》
  89. for (int i = 0; i < 10; i++) {
  90. Person person = new Person();
  91. person.setId(i + 1);
  92. person.setName(“name” + i);
  93. personList.add(person);
  94. }
  95. when(sayDao.queryPerson(any(Person.class))).thenReturn(personList);
  96. // 2.验证
  97. Person query = new Person();
  98. query.setId(13);
  99. query.setName(“Leifeng”);
  100. List<Person> list = sayService.queryPerson(query);
  101. assertTrue(10 == list.size());
  102. // 重要(根据具体业务设计)
  103. assertTrue(3 == list.get(3).getFlag());
  104. }
  105. }

/** * 类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指定这个监听器来实现单元测试

  1. package org.springframework.test.context.support;
  2. import java.lang.annotation.Annotation;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.lang.reflect.Method;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import static org.mockito.Mockito.mock;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.test.context.TestContext;
  11. /**
  12. * @author yanglin lv
  13. */
  14. public class DependencyInjectionAsMockitoTestExecutionListener extends DependencyInjectionTestExecutionListener {
  15. private static String SETTER = “set”;
  16. private static String GETTER = “get”;
  17. @Override
  18. protected void injectDependencies(final TestContext testContext) throws Exception {
  19. super.injectDependencies(testContext);
  20. Object bean = testContext.getTestInstance();
  21. Class[] mockClass = getMockClass(bean.getClass());
  22. Method[] methods = bean.getClass().getDeclaredMethods();
  23. Class clz = bean.getClass();
  24. Object instance = null;
  25. List<MockObjectMap> objs = new ArrayList<MockObjectMap>();
  26. autowireMockBean(clz, bean, objs);
  27. List<Object> stubObjs = getStubInstance(clz, bean);
  28. autowireMockBeanForSpring(stubObjs, objs);
  29. }
  30. private void autowireMockBeanForSpring(List<Object> stubObjs, List<MockObjectMap> objs)
  31. throws IllegalArgumentException,
  32. IllegalAccessException,
  33. InvocationTargetException {
  34. for (Object object : stubObjs) {
  35. Class claz = object.getClass();
  36. do {
  37. for (Method method : claz.getDeclaredMethods()) {
  38. if (method.getName().startsWith(SETTER)) {
  39. for (MockObjectMap mockObjectMap : objs) {
  40. Object obj = method.getGenericParameterTypes()[0];
  41. if (obj instanceof java.lang.reflect.Type
  42. && mockObjectMap.getType().getName().equalsIgnoreCase(((Class) obj).getName())) {
  43. method.invoke(object, mockObjectMap.getObj());
  44. continue;
  45. }
  46. }
  47. }
  48. }
  49. claz = claz.getSuperclass();
  50. } while (!claz.equals(Object.class));
  51. }
  52. }
  53. private void autowireMockBean(Class clz, Object bean, List<MockObjectMap> objs) throws IllegalArgumentException,
  54. IllegalAccessException {
  55. for (Field field : clz.getFields()) {
  56. Annotation[] mockAnnotations = field.getAnnotations();
  57. for (Annotation annotation : mockAnnotations) {
  58. if (annotation instanceof org.mockito.Mock) {
  59. MockObjectMap mockObjectMap = new MockObjectMap();
  60. objs.add(mockObjectMap);
  61. mockObjectMap.setType(field.getType());
  62. mockObjectMap.setObj(mock(field.getType()));
  63. field.setAccessible(true);
  64. field.set(bean, mockObjectMap.getObj());
  65. continue;
  66. }
  67. }
  68. }
  69. }
  70. /**
  71. * 取得测试类中所有的mock对象的类型
  72. *
  73. * @param clazz
  74. * @return
  75. */
  76. private Class[] getMockClass(Class claz) {
  77. List<Class> clasList = new ArrayList<Class>();
  78. Field[] fields = claz.getDeclaredFields();
  79. for (Field field : fields) {
  80. Annotation[] mockAnnotations = field.getAnnotations();
  81. for (Annotation annotation : mockAnnotations) {
  82. if (annotation instanceof org.mockito.Mock) {
  83. clasList.add(field.getType());
  84. continue;
  85. }
  86. }
  87. }
  88. return clasList.toArray(new Class[0]);
  89. }
  90. /**
  91. * 取得测试类中测试桩类
  92. *
  93. * @param clazz
  94. * @return
  95. * @throws InvocationTargetException
  96. * @throws IllegalAccessException
  97. * @throws IllegalArgumentException
  98. */
  99. private List<Object> getStubInstance(Class clazz, Object bean) throws IllegalArgumentException,
  100. IllegalAccessException, InvocationTargetException {
  101. List<Object> objList = new ArrayList<Object>();
  102. Field[] fields = clazz.getDeclaredFields();// 测试类中所有的域名
  103. Method[] methods = clazz.getDeclaredMethods();
  104. for (Field field : fields) {
  105. Annotation[] mockAnnotations = field.getAnnotations();
  106. for (Annotation annotation : mockAnnotations) {
  107. if (annotation instanceof Autowired) {
  108. for (Method method : methods) {
  109. String name = field.getName();
  110. if (method.getName().startsWith(GETTER) && method.getName().substring(3).equalsIgnoreCase(name)) {
  111. objList.add(method.invoke(bean, null)); // 将所有的测试桩类放在objList
  112. }
  113. }
  114. }
  115. }
  116. }
  117. return objList;
  118. }
  119. private class MockObjectMap {
  120. private Object   obj;
  121. private Class<?> type;
  122. public Object getObj() {
  123. return obj;
  124. }
  125. public void setObj(Object obj) {
  126. this.obj = obj;
  127. }
  128. public Class<?> getType() {
  129. return type;
  130. }
  131. public void setType(Class<?> type) {
  132. this.type = type;
  133. }
  134. }
  135. }

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类设置期望输出简单实现

来源: <http://lvyanglin.iteye.com/blog/1025956>  

From WizNote

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,960
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,484
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,330
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,113
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,745
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,779