Издевательствовать свойство проксированного сервиса CGLIB не работает - программирование
Подтвердить что ты не робот

Издевательствовать свойство проксированного сервиса CGLIB не работает

У меня возникла проблема при попытке издеваться над свойством службы из теста 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);
    }
}

Mocking fooDao не влияет, поскольку результат не является ожидаемым. Вот код и сервиса, и 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";
    }
}

Как мы видим, фактическая служба предназначена для возврата "foo", но тест издевается над dao, поэтому служба возвращает "var". Я знаю, что это связано с прокси-сервером CGLIB, но я не могу понять, как заставить его работать, не используя setter для свойства fooDao. Любая помощь будет оценена.

С уважением и благодарностью заранее.

4b9b3361

Ответ 1

Короткий ответ

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

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

unwrapFooService() можно определить следующим образом:

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

... long one

Проблема довольно сложная, но разрешимая. Как вы уже догадались, это побочный эффект использования прокси-серверов CGLIB. В принципе, Spring создает подкласс вашего FooServiceImpl с именем, похожим на FooServiceImpl$EnhancerByCGLIB. Этот подкласс содержит ссылку на исходный FooServiceImpl, а также... все поля FooServiceImpl имеют (что понятно - это подкласс).

Таким образом, на самом деле существуют две переменные: FooServiceImpl$EnhancerByCGLIB.fooDao и FooServiceImpl.fooDao. Вы назначаете макет первому, но ваша служба использует последнее... Я написал об этой подводной камне некоторое время назад.