Is it true that mockito can't mock objects that were already enhanced by CGLIB?
public class Article {
@Autowired
private dbRequestHandler
@Autowired
private filesystemRequestHandler
@Transactional
public ArticleDTO getArticleContents() {
//extractText() and then save the data in DTO
//extractImages() and then save the data in DTO
// some other calls to other databases to save data in dto
return articleDTO;
}
public void extractText() {
//call to DB
}
public void extractImages() {
// call to file system
}
}
public class IntegrationTest {
@Autowired
private Article article;
//setup method {
articleMock = Mockito.spy(article);
doNothing().when(articleMock).extractImages();
}
}
In the above example when it comes to doNothing().when(articleMock).extractImages();
it actually calls the real function. On a closer look articleMock gets enhanced two times. One cause of autowiring
and second time cause of spying
.
If I can't spy on enhaced objects, then how can I test the getArticle()
method in my Integration test, so that I can verify a proper DTO is returned.
Note : I actually don't want to test the method which does filesystem calls. just the DB ones. thats why I need to test the getArticle
method.
If you run as a true unit test and not as an integration test, you need not run in a container having Spring autowire for you. In one of your comments, I think you alluded to trying this, and you noted that there was an endless set of chained object references which you would have to provide as well. But there is a way around that. Mockito provides some predefined
Answer
classes that you can initialize your mock with. You may want to look at RETURNS_DEEP_STUBS, which will possibly get you around this problem.Will you please update your question with ready-to-go compilable code. Here's some code review suggestions:
Issues with this question code:
Here's what you maybe should use as you questionCode with the above issues fixed:
Article.java
IntegrationTest.java is a poor name for a testClass because it's to generic. I would suggest ArticleTest for a java unit test.
ArticleTest.java
If I understand correctly your class is wired by Spring. Spring uses CGLIB to ensure transactional behaviour only if there is no interface, which is implemented by your object. If there is an interface, it uses simple JDK Dynamic Proxies. (see http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html)
Maybe you could try to extract an interface, and let Spring to use dynamic proxies. Maybe then Mockito could perform better.
You can utilize AdditionalAnswers.delegatesTo method. In following example, the
secondProxyDoingMocking
declaration creates something like a spy (compare with implementation ofspy()
method) except it uses "lightweight" method delegation.I didn't test this example, however I assembled it from my working code. My use case was similar: return constant value for given method, call real method for all remaining methods of Spring
@Transactional
-annotated bean.