I am writing a TestNG test case in a spring project and use jmockit to mock dependencies. I encounter a problem, that is I cannot use Deencapsulation.setField to set dependency to a spring-managed object contain a @Transactional method, the error is:
No instance field of name "dataLogManager" found in class java.lang.reflect.Proxy
My sample code listed here,
Test case:
public class QueueManagerTest extends AbstractTestNGSpringContextTests {
@Autowired
private QueueManager queueManager;
@Autowired
private ApplicationContext context;
@Autowired
private TransInfoManager transInfoManager;
@Autowired
private Generator generator;
@Autowired
private MessageDTOFactoryBean messageDTOFactoryBean;
@Test(description="單元測試:寫入一筆Data資料,非邊界情況下(無待處理筆數)")
public void writeControlMessage(
@Mocked final TransInfoManager transInfoManager,
@Mocked final DataManager dataManager,
@Mocked final DataLogManager dataLogManager
) {
....
Deencapsulation.setField(queueManager, "dataLogManager", dataLogManager);
Deencapsulation.setField(queueManager, "dataManager", dataManager);
Deencapsulation.setField(queueManager, "transInfoManager", transInfoManager);
....
}
}
Service:
@Service
public interface QueueManager {
@Transactional
MessageDTO putDataIntoQueue(MessageDTO message);
}
Anybody knows how to solve this problem? Thank you.
If what you want to test is some specific class implementing the QueueManager
interface, then a solution is simply to instantiate the class directly in the test, rather than using Spring for that.
The reason for the exception is that Spring actually creates a proxy class instance for the queueManager
field, intended to decorate the real implementation (the proxy object delegates every method call to another object, which would be an instance of an application class actually implementing the QueueManager
interface). The proxy class does not contain a field named "dataLogManager", so that's why Deencapsulation.setField(...)
can't find it.
If the first solution I mentioned is not applicable, you can still let Spring do the instantiation, provided it also inject the fields inside the queueManager
object. To mock the class implementing DataLogManager
(and other interfaces), use @Capturing
instead of (or in addition to) @Mocked
; this will instruct JMockit to extend mocking to all implementation classes.