В JUnit с Spring, как мне создать шпиона для @Service с помощью Mockito (1.10.18)?

Я использую Spring 3.2.11.RELEASe, JUnit 4.12 и Mockito 1.10.18. Как в моем тесте JUnit создать шпиона (не макета, шпиона) весенней службы @Autowired? Вот как объявляется услуга…

@Service("orderService")
public class OrderServiceImpl implements OrderService, InitializingBean 
{

а вот как настроен мой тест JUnit…

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class ProcessPDWorkerTest extends AbstractWorkerTest
{
    …    
    @Autowired
    protected OrderService m_orderSvc;

с

      final OrderService orderSvcSpy = Mockito.spy(getTargetObject(m_orderSvc));
    …
      ReflectionTestUtils.setField(workerObj, "m_orderSvc", orderSvcSpy);

где у меня…

protected static <T> T getTargetObject(Object proxy)
{
    if ((AopUtils.isJdkDynamicProxy(proxy)))
    {
        try
        {
            return (T) getTargetObject(((Advised) proxy).getTargetSource().getTarget());
        }
        catch (Exception e)
        {
            throw new RuntimeException("Failed to unproxy target.", e);
        }
    }
    return (T) proxy;
}

но я получаю следующее исключение в строке «Mockito.spy(getTargetObject(m_orderSvc))»:

java.lang.ClassCastException: org.mainco.subco.myproject.service.OrderServiceImpl cannot be cast to java.lang.Class
    at org.mainco.subco.test.worker.AbstractWorkerTest.createMockOrders(AbstractWorkerTest.java:146)
    at org.mainco.subco.orders.ProcessPDWorkerTest.mockTrainingAssignmentAndOrder(ProcessPDWorkerTest.java:1117)
    at org.mainco.subco.orders.ProcessPDWorkerTest.testCreateTrainingSessionWTrainer(ProcessPDWorkerTest.java:297)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

person Dave    schedule 24.07.2015    source источник
comment
Почему вы хотите шпионить за зависимостью? Шпион действительно имеет смысл только при тестировании логики внутри самого класса. Если вам нужен настоящий OrderService, просто используйте Autowired, иначе вы можете смоделировать класс (без использования Spring DI) и в конечном итоге заглушить один метод как callRealMethod.   -  person Gualtiero Testa    schedule 26.07.2015
comment
Мне нужны некоторые настоящие методы OrderService, но я хочу издеваться над другими. Это то, для чего нужен шпион, или в этой версии Mockito есть другая концепция, эквивалентная шпиону?   -  person Dave    schedule 27.07.2015
comment
Прошу прощения, но я не могу понять, какая строка кода вызывает исключение. Вы можете это указать?   -  person Jose Martinez    schedule 28.07.2015
comment
Вы уверены, что getTargetObject() возвращает экземпляр OrderService?   -  person Fred Porciúncula    schedule 28.07.2015
comment
#getTargetObject должен использовать вывод типа j7, но из-за отсутствия ссылки возвращаемый тип — Object. делая это: final OrderService targetObject = getTargetObject(m_orderSvc) решит эту проблему   -  person hahn    schedule 29.07.2015
comment
вы можете ознакомиться с шагами, которые предпринимаются для определения типа, здесь docs.oracle.com/javase/tutorial/java/generics/   -  person hahn    schedule 29.07.2015
comment
или даже лучшим решением было бы изменить подпись метода с getTargetObject (прокси объекта) на getTargetObject (прокси T).   -  person hahn    schedule 29.07.2015


Ответы (2)


В качестве альтернативы вы можете попробовать изменить только эту строку:

 final OrderService orderSvcSpy = Mockito.spy((OrderService)getTargetObject(m_orderSvc));
 …

Это должно работать. Mockito.spy() — это перегруженный статический метод, и компилятор выбирает Mockito.spy(Class) во время компиляции.

person Alex Borysov    schedule 29.07.2015

Работает на меня:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class ProcessPDWorkerTest extends AbstractWorkerTest {

    …    
    @Spy
    @Autowired
    protected OrderService m_orderSvc;

    @Before
    public void initMocks() {
        MockitoAnnotations.initMocks(this);
        ReflectionTestUtils.setField(workerObj, "m_orderSvc", m_orderSvc);
    }
person Alex Borysov    schedule 29.07.2015
comment
сообщение с вопросом показывает, что есть #getTargetObject, который извлекает исходный объект в случае прокси-объекта - person hahn; 29.07.2015
comment
Понятно, но я не уверен, требуется ли вызов #getTargetObject в тестовой логике, потому что в большинстве случаев тесты на самом деле не заботятся о том, является ли он целевым или прокси-объектом. - person Alex Borysov; 29.07.2015