Mocking Improved CGLIB Objects

Is it true that mockito cannot mimic objects that have already been 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 a real function. With a closer look, articleMock is doubled. One of the reasons for autowiring and the second reason for being spying .

If I can’t keep track of the improved objects, then how can I test the getArticle() method in my Integration test so that I can check if the correct DTO is being returned.

Note. Actually, I do not want to test a method that calls file system calls. only db. this is why i need to test getArticle method.

+6
source share
4 answers

If I understand correctly, your class is connected to Spring. Spring uses CGLIB to provide transactional behavior only if there is no interface that your object implements. If there is an interface, it uses the simple JDK Dynamic Proxies. (see http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html )

Perhaps you can try to extract the interface and let Spring use dynamic proxies. Perhaps then Mokito could work better.

+3
source

If you use true unit test, and not as an integration test, you do not need to run in a container that has Spring autowire for you. In one of your comments, I think you referenced this, and you noticed that there is an infinite set of anchor references to objects that you would also have to provide. But there is a way around this. Mockito provides some predefined Answer classes that you can use to initialize your layout. You can look at RETURNS_DEEP_STUBS , which might help you solve this problem.

+1
source

Update your question with ready-to-use code. Here are some code review guidelines:

Problems with this question code:

  • Article.java missing import: org.springframework.beans.factory.annotation.Autowired
  • Article.java missing import: org.springframework.transaction.annotation.Transactional
  • Article.java attribute syntax problem: dbRequestHandler
  • Article.java: filesystemRequestHandler attribute syntax problem
  • The Article.java method does not have an initialized return statement: articleDTO

Here is what you might need to use when you asked a question with the above problems:

Article.java

 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; public class Article { @Autowired private Object dbRequestHandler; @Autowired private Object 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 ArticleDTO articleDTO = null; return articleDTO; } public void extractText() { // call to DB } public void extractImages() { // call to file system } } 

IntegrationTest.java is a bad name for testClass because it is common. I would suggest ArticleTest for java unit test.

ArticleTest.java

 import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.beans.factory.annotation.Autowired; @RunWith(PowerMockRunner.class) @PrepareForTest(ClassWithPrivate.class) public class ArticleTest { @InjectMocks private Article cut; @Mock private Object dbRequestHandler; @Mock private Object filesystemRequestHandler; @Test public void testeExtractImages() { /* Initialization */ Article articleMock = Mockito.spy(cut); /* Mock Setup */ Mockito.doNothing().when(articleMock).extractImages(); /* Test Method */ ArticleDTO result = cut.getArticleContents(); /* Asserts */ Assert.assertNull(result); } } 
+1
source

You can use the AdditionalAnswers.delegatesTo method. In the following example, the secondProxyDoingMocking creates something like a spy (compared to the implementation of the spy() method), except that it uses the delegation of the "light" method.

 import org.mockito.AdditionalAnswers; public class ArticleTest { @Autowired private Article firstProxyDoingAutowiring; @Test public void testExtractImages() { Article secondProxyDoingMocking = Mockito.mock(Article.class, Mockito.withSettings().defaultAnswer( AdditionalAnswers.delegatesTo(firstProxyDoingAutowiring) ) ); Mockito.doNothing().when(secondProxyDoingMocking).extractImages(); ... } } 

I did not test this example, however I compiled it from my working code. My use case was similar: returning a constant value for a given method, calling a real method for all other Spring @Transactional -annotated bean methods.

+1
source

Source: https://habr.com/ru/post/957217/


All Articles