form - taglib spring jsp
Simular una propiedad de un servicio de proxy CGLIB no funciona (1)
Tengo un problema al intentar simular una propiedad de un servicio desde una prueba de Junit:
@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);
}
}
La burla de fooDao no tiene ningún efecto ya que el resultado no es el esperado. Aquí está el código tanto del servicio como del 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, el servicio real está destinado a devolver "foo", pero la prueba se burla del dao, por lo que el servicio devuelve "var". Sé que es una cosa relacionada con el proxy CGLIB, pero no puedo averiguar cómo hacer que funcione sin usar un setter para la propiedad fooDao. Cualquier ayuda sería apreciada.
Saludos y gracias de antemano.
Respuesta corta
Debe desenvolver el proxy y establecer 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;
}
...largo
El problema es bastante complejo, pero solucionable. Como ha adivinado, este es un efecto secundario de los proxies CGLIB que se están utilizando. En principio, Spring crea una subclase de su FooServiceImpl
llamada similar a FooServiceImpl$EnhancerByCGLIB
. Esta subclase contiene una referencia al FooServiceImpl
original y también a ... todos los campos que tiene FooServiceImpl
(lo cual es comprensible, esta es una subclase).
Entonces, en realidad hay dos variables: FooServiceImpl$EnhancerByCGLIB.fooDao
y FooServiceImpl.fooDao
. Está asignando un simulacro al primero, pero su servicio usa el segundo ... Hace poco wrote sobre estos escollos.