基于spring與mockito單元測試Mock對象注入
1.關鍵詞 單元測試、spring、mockito 2.概述 單元測試目前已經成為項目中保證代碼質量的一種必要方法,對于一些不易構造或者不易獲取的對象通過mock的方式進行測試是一種很有效的處理辦法。在基于spring的mock測試中,mock對象獲取和使用的便利性可以提升單元測試代碼的質量。 3.實現原理 Mock對象的注入使用注解和反射,對象注入依賴spring框架支持junit4提供的TestExcutionListeners監聽器對象,在監聽器中將mock對象注入到單元測試類中。 4.新建對象方式代碼private IAccessServiceaccessService = Mockito.mock(IAccessService.class);@BeforeClasspublic static void beforeClass(){// 構造并注入Mock對象ICmsProxyServicesceneConfigService.setAccessService(accessService);}
5.監聽器方式代碼 5.1 實現監聽器 繼承DependencyInjectionTestExecutionListener類, 實現injectDependencies(TestContexttestContext)方法public class MockitoDependencyInjectionTestExecutionListener extendsDependencyInjectionTestExecutionListener {@Overrideprotected void injectDependencies(TestContext testContext) throws Exception {super.injectDependencies(testContext);init(testContext);}
5.2 利用反射注入mock對象private void init(final TestContext testContext)throws Exception {Object bean = testContext.getTestInstance();Field[] fields = bean.getClass().getDeclaredFields();for (Field field : fields) {Annotation[] annotations = field.getAnnotations();for (Annotation annotation : annotations) {if(annotationinstanceof Mock){//注入Mock實例MockObject obj = new MockObject();obj.setType(field.getType());obj.setObj(Mockito.mock(field.getType()));field.setAccessible(true);field.set(bean, obj.getObj());mockObjectMap.put(field.getName(), obj);}else if(annotation instanceofAutowired){injectFields.add(field);}}}
AutowireCapableBeanFactory factory =testContext.getApplicationContext().getAutowireCapableBeanFactory();//Autowired注解注入mock對象for (Field field :injectFields) {field.setAccessible(true);Object object = field.get(bean);if(objectinstanceof Proxy){Class targetClass = AopUtils.getTargetClass(object);if(targetClass ==null)return;Field[] targetFields =targetClass.getDeclaredFields();for(Field targetField : targetFields){targetField.setAccessible(true);if(mockObjectMap.get(targetField.getName()) ==null){continue;}targetField.set(getTargetObject(object,mockObjectMap.get(targetField.getName()).getType()),mockObjectMap.get(targetField.getName()).getObj());}}else{Object realObject = factory.getBean(field.getName());if(null != realObject){Method[] methods = realObject.getClass().getDeclaredMethods();for (Method method : methods) {if(method.getName().equalsIgnoreCase("set" +field.getName())){method.invoke(realObject, mockObjectMap.get(field.getName()).getObj());}}}}}}
5.3 測試類配置 使用@TestExecutionListeners注解,引入監聽器,需要mock的對象加上@Mock注解。@TestExecutionListeners({MockitoDependencyInjectionTestExecutionListener.class})public class AccessServiceImplTest extends BaseTestCase{@Autowiredprivate IAccessServiceaccessService;@Mockprivate IPersonServicepersonService;@Mockprivate IAccessDaoaccessDao;}
6.總結 監聽器的方式解放了代碼中硬編碼注入mock對象,使得代碼簡潔干凈。
private IAccessServiceaccessService = Mockito.mock(IAccessService.class); @BeforeClass public static void beforeClass(){ // 構造并注入Mock對象ICmsProxyService sceneConfigService.setAccessService(accessService); } |
public class MockitoDependencyInjectionTestExecutionListener extends DependencyInjectionTestExecutionListener { @Override protected void injectDependencies(TestContext testContext) throws Exception { super.injectDependencies(testContext); init(testContext); } |
private void init(final TestContext testContext)throws Exception { Object bean = testContext.getTestInstance(); Field[] fields = bean.getClass().getDeclaredFields(); for (Field field : fields) { Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { if(annotationinstanceof Mock){ //注入Mock實例 MockObject obj = new MockObject(); obj.setType(field.getType()); obj.setObj(Mockito.mock(field.getType())); field.setAccessible(true); field.set(bean, obj.getObj()); mockObjectMap.put(field.getName(), obj); }else if(annotation instanceofAutowired){ injectFields.add(field); } } } |
AutowireCapableBeanFactory factory =testContext.getApplicationContext().getAutowireCapableBeanFactory(); //Autowired注解注入mock對象 for (Field field :injectFields) { field.setAccessible(true); Object object = field.get(bean); if(objectinstanceof Proxy){ Class targetClass = AopUtils.getTargetClass(object); if(targetClass ==null) return; Field[] targetFields =targetClass.getDeclaredFields(); for(Field targetField : targetFields){ targetField.setAccessible(true); if(mockObjectMap.get(targetField.getName()) ==null){ continue; } targetField.set(getTargetObject(object,mockObjectMap.get(targetField.getName()).getType()),mockObjectMap.get(targetField.getName()).getObj()); } }else{ Object realObject = factory.getBean(field.getName()); if(null != realObject){ Method[] methods = realObject.getClass().getDeclaredMethods(); for (Method method : methods) { if(method.getName().equalsIgnoreCase("set" +field.getName())){ method.invoke(realObject, mockObjectMap.get(field.getName()).getObj()); } } } } } } |
5.3 測試類配置
使用@TestExecutionListeners注解,引入監聽器,需要mock的對象加上@Mock注解。
@TestExecutionListeners({MockitoDependencyInjectionTestExecutionListener.class}) public class AccessServiceImplTest extends BaseTestCase{ @Autowired private IAccessServiceaccessService; @Mock private IPersonServicepersonService; @Mock private IAccessDaoaccessDao; } |
6.總結
監聽器的方式解放了代碼中硬編碼注入mock對象,使得代碼簡潔干凈。
posted on 2014-03-07 10:39 順其自然EVO 閱讀(4992) 評論(0) 編輯 收藏 所屬分類: 測試學習專欄