1/* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5package org.mockito.internal.configuration; 6 7import org.mockito.*; 8import org.mockito.configuration.AnnotationEngine; 9import org.mockito.exceptions.Reporter; 10import org.mockito.exceptions.base.MockitoException; 11import org.mockito.internal.util.MockUtil; 12import org.mockito.internal.util.reflection.FieldInitializationReport; 13import org.mockito.internal.util.reflection.FieldInitializer; 14 15import java.lang.annotation.Annotation; 16import java.lang.reflect.Field; 17 18import static org.mockito.Mockito.withSettings; 19 20/** 21 * Process fields annotated with @Spy. 22 * 23 * <p> 24 * Will try transform the field in a spy as with <code>Mockito.spy()</code>. 25 * </p> 26 * 27 * <p> 28 * If the field is not initialized, will try to initialize it, with a no-arg constructor. 29 * </p> 30 * 31 * <p> 32 * If the field is also annotated with the <strong>compatible</strong> @InjectMocks then the field will be ignored, 33 * The injection engine will handle this specific case. 34 * </p> 35 * 36 * <p>This engine will fail, if the field is also annotated with incompatible Mockito annotations. 37 */ 38@SuppressWarnings({"unchecked"}) 39public class SpyAnnotationEngine implements AnnotationEngine { 40 41 public Object createMockFor(Annotation annotation, Field field) { 42 return null; 43 } 44 45 @SuppressWarnings("deprecation") // for MockitoAnnotations.Mock 46 public void process(Class<?> context, Object testInstance) { 47 Field[] fields = context.getDeclaredFields(); 48 for (Field field : fields) { 49 if (field.isAnnotationPresent(Spy.class) && !field.isAnnotationPresent(InjectMocks.class)) { 50 assertNoIncompatibleAnnotations(Spy.class, field, Mock.class, org.mockito.MockitoAnnotations.Mock.class, Captor.class); 51 Object instance = null; 52 try { 53 FieldInitializationReport report = new FieldInitializer(testInstance, field).initialize(); 54 instance = report.fieldInstance(); 55 } catch (MockitoException e) { 56 new Reporter().cannotInitializeForSpyAnnotation(field.getName(), e); 57 } 58 try { 59 if (new MockUtil().isMock(instance)) { 60 // instance has been spied earlier 61 // for example happens when MockitoAnnotations.initMocks is called two times. 62 Mockito.reset(instance); 63 } else { 64 field.setAccessible(true); 65 field.set(testInstance, Mockito.mock(instance.getClass(), withSettings() 66 .spiedInstance(instance) 67 .defaultAnswer(Mockito.CALLS_REAL_METHODS) 68 .name(field.getName()))); 69 } 70 } catch (IllegalAccessException e) { 71 throw new MockitoException("Problems initiating spied field " + field.getName(), e); 72 } 73 } 74 } 75 } 76 77 //TODO duplicated elsewhere 78 void assertNoIncompatibleAnnotations(Class annotation, Field field, Class... undesiredAnnotations) { 79 for (Class u : undesiredAnnotations) { 80 if (field.isAnnotationPresent(u)) { 81 new Reporter().unsupportedCombinationOfAnnotations(annotation.getSimpleName(), annotation.getClass().getSimpleName()); 82 } 83 } 84 } 85} 86