@Component public class PostDistrictFinder { public String getDistrict(String letter) { // this will involve some complex lookup into an even more complex Cobol // based system return "SOME DISTRICT"; } } @Component public class PostCentral { @Autowired private PostDistrictFinder finder; //This is just a plain annotation to force Spring to make a proxy @Timed public void processLetter(String letter) { // process letter, e.g. calculate whether postage is correct or send out // invoice, validate address as known etc. String district = finder.getDistrict(letter); System.err.println(district); // forward letter to district } } public class Main { public static void main(final String[] args) { final ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/mock-context.xml"); final PostCentral bean = context.getBean(PostCentral.class); bean.processLetter(""); } }Essentially it is just one bean using another bean. Running this code will print "SOME DISTRICT".
Say I have this test, what do you think it prints?:
@ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class MockServiceTest { @Mock private PostDistrictFinder pdf; @Autowired @InjectMocks private PostCentral poc; @Before public void setup(){ MockitoAnnotations.initMocks(this); Mockito.when(pdf.getDistrict(Mockito.anyString())).thenReturn("Mocked value"); } @Test public void test() throws Exception { poc.processLetter(""); } }Does it print "SOME DISTRICT" or "Mocked value"? What do you think it prints if you remove the @Timed annotation on the processLetter in the PostCentral class?
Well the above method prints "SOME DISTRICT" and not "Mocked value" with the @Timed annotation on the method and "Mocked value" without the @Timed annotation (note that the result would have been the same had I used @Transactional).
Why is that? The reason is that Spring will create a proxy of the PostCentral class if it is annotated with @Timed (or @Transaction) or any other annotation that causes a proxy to be created. Set a break point in the code and verify if you are in doubt.
How do you get the test to work? You might think that this is enough:
@Before public void setup(){ MockitoAnnotations.initMocks(this); ReflectionTestUtils.setField(poc, "finder", pdf); Mockito.when(pdf.getDistrict(Mockito.anyString())).thenReturn("Mocked value"); }However, this is not the case, as you are operating on the proxy and not the proxied object. You can however do this:
//REMOVE THE @InjectMocks FROM THE PostCentral (poc) FIELD @Before public void setup() throws Exception{ MockitoAnnotations.initMocks(this); PostCentral pc = (PostCentral) unwrapProxy(poc); ReflectionTestUtils.setField(pc, "finder", pdf); Mockito.when(pdf.getDistrict(Mockito.anyString())).thenReturn("Mocked value"); } //http://forum.springsource.org/showthread.php?60216-Need-to-unwrap-a-proxy-to-get-the-object-being-proxied public static final Object unwrapProxy(Object bean) throws Exception { /* * If the given object is a proxy, set the return value as the object * being proxied, otherwise return the given object. */ if (AopUtils.isAopProxy(bean) && bean instanceof Advised) { Advised advised = (Advised) bean; bean = advised.getTargetSource().getTarget(); } return bean; }If the above makes sense, then fine. If you want to understand the inner workings of Spring AOP/proxies, then read this http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies.