복붙노트

[SPRING] 프록시가 작동하지 않는 CGLIB 서비스의 자산 조롱

SPRING

프록시가 작동하지 않는 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);
    }
}

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"를 반환하기위한 것이지만, 테스트는 "var"를 반환하도록 DAO를 조롱합니다. CGLIB 프록시 관련 일을 알고 있지만 fooDao 속성에 setter를 사용하지 않고 작동시키는 방법을 알 수는 없습니다. 어떤 도움을 주시면 감사하겠습니다.

감사와 사전에 감사드립니다.

해결법

  1. ==============================

    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;
    }
    

    문제는 상당히 복잡하지만 해결할 수 있습니다. 짐작 하시겠지만 이것은 CGLIB 프록시의 부작용입니다. 원칙적으로 Spring은 FooServiceImpl $ EnhancerByCGLIB와 비슷한 이름으로 FooServiceImpl의 하위 클래스를 만듭니다. 이 서브 클래스에는 원래 FooServiceImpl에 대한 참조뿐만 아니라 FooServiceImpl에있는 모든 필드가 포함됩니다 (이것은 이해할 수 있습니다 - 이것은 서브 클래스입니다).

    실제로 FooServiceImpl $ EnhancerByCGLIB.fooDao와 FooServiceImpl.fooDao라는 두 가지 변수가 있습니다. 당신은 이전에 mock을 할당하고 있지만 서비스는 후자를 사용합니다 ... 나는이 함정에 대해 몇 시간 전에 썼습니다.

  2. from https://stackoverflow.com/questions/9033874/mocking-a-property-of-a-cglib-proxied-service-not-working by cc-by-sa and MIT license