2012-01-27 7 views
16

Estoy teniendo un problema cuando se trata de burlarse de una propiedad de un servicio desde el interior de una prueba Junit proxy:que imita una propiedad de un CGLIB servicio no trabaja

@ContextConfiguration("classpath:application-config.xml") 
@RunWith(SpringJUnit4ClassRunner.class) 
public class FooServiceTests { 

    @Autowired 
    private FooServiceImpl fooService; 

    @Test 
    public void testFoo() { 
     String str = fooService.foo(); 
     assertEquals("Var", str); 
    } 

    @Before 
    public void mockFooDao() throws Exception { 
     FooDao mockFooDao = Mockito.mock(FooDao.class); 
     Mockito.when(mockFooDao.foo()).thenReturn("Var"); 
     ReflectionTestUtils.setField(fooService, "fooDao", mockFooDao); 
    } 
} 

que imita fooDao no tiene ningún efecto ya que el resultado no es lo esperado Aquí está el código de ambos, el servicio y la DAO:

@Service("fooService") 
public class FooServiceImpl implements FooService { 

    @Autowired 
    protected FooDao fooDao; 

    @Override 
    public String foo() { 
     return fooDao.foo(); 
    } 
} 

@Repository 
public class FooDaoImpl implements FooDao { 

    @Override 
    public String foo() { 
     return "foo"; 
    } 
} 

Como podemos ver en el servicio real está destinado a volver "foo", pero la prueba se burla de la DAO por lo que el servicio devuelve "var". Sé que es una cuestión relacionada con el proxy CGLIB, pero no puedo encontrar la manera de hacerlo funcionar sin utilizar un setter para la propiedad fooDao. Cualquier ayuda sería apreciada.

Saludos y gracias de antemano.

Respuesta

38

Respuesta corta

Tienes que unwrap the proxy y configura el campo en el objeto de destino:

ReflectionTestUtils.setField(unwrapFooService(), "fooDao", mockFooDao); 

El unwrapFooService() se puede definir de la siguiente manera:

private FooServiceImpl unwrapFooService() { 
    if(AopUtils.isAopProxy(fooService) && fooService instanceof Advised) { 
     Object target = ((Advised) fooService).getTargetSource().getTarget(); 
     return (FooServiceImpl)target; 
    } 
    return null; 
} 

... durante mucho tiempo uno

El problema es bastante complejo, pero por lo lvable. Como ha adivinado, este es un efecto secundario de los proxies CGLIB que se utilizan. En principio, Spring crea una subclase de FooServiceImpl llamada similar a FooServiceImpl$EnhancerByCGLIB. Esta subclase contiene una referencia al original FooServiceImpl, así como ... todos los campos FooServiceImpl tienen (lo cual es comprensible: esta es una subclase).

Por lo tanto, en realidad existen dos variables: FooServiceImpl$EnhancerByCGLIB.fooDao y FooServiceImpl.fooDao. Usted está asignando un simulacro a la primera, pero su servicio utiliza la última ... I wrote sobre este peligro hace algún tiempo.

+0

Sí! ¡Funcionó! Muchas gracias Tomasz. – franDayz

+0

@ frandiaz83: ¡me alegro de poder ayudar! Considere [aceptar] (http://meta.stackexchange.com/questions/5421) y/o vote la respuesta correcta para señalar a los lectores futuros a la solución adecuada. –

+0

Sí, por supuesto. He aceptado la respuesta, pero no puedo votar porque no tengo suficiente reputación ... Gracias de nuevo. – franDayz