模拟CGLIB代理服务的属性不起作用

时间:2012-01-27 13:24:03

标签: spring mocking cglib proxies

尝试在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”,但是测试会模仿dao,因此服务返回“var”。我知道这是一个与CGLIB代理相关的东西,但我无法弄清楚如何在不使用fooDao属性的setter的情况下使其工作。任何帮助将不胜感激。

提前致意并表示感谢。

1 个答案:

答案 0 :(得分:39)

简短回答

您必须unwrap the proxy并在目标对象上设置字段:

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的子类,其名称类似于FooServiceImpl$EnhancerByCGLIB。此子类包含原始FooServiceImpl引用以及... FooServiceImpl所有字段(这是可以理解的 - 这是一个子类)。

因此实际上有两个变量:FooServiceImpl$EnhancerByCGLIB.fooDaoFooServiceImpl.fooDao。你正在为前者分配一个模拟,但是你的服务使用了后者...我wrote关于这个陷阱,不久前。