使用Mockito无法模拟CGLIB代理服务的属性

18

我在尝试模拟一个服务的属性时,遇到了问题:

@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",但是测试mock了dao,所以服务返回了"var"。我知道这与CGLIB代理有关,但我无法弄清楚如何在不使用fooDao属性的setter的情况下使其正常工作。任何帮助将不胜感激。

谢谢!

1个回答

41

简短回答

您需要取消代理并在目标对象上设置该字段:

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.fooDaoFooServiceImpl.fooDao。你给前者分配了一个mock,但是你的服务使用的是后者...我之前写过关于这个陷阱的文章。


@frandiaz83:很高兴我能帮到你!考虑接受和/或点赞正确的答案,以指向未来读者的正确解决方案。 - Tomasz Nurkiewicz
当然可以。我已经接受了答案,但是由于我的声望不够,所以无法投票支持...再次感谢。 - franDayz
unwrapFooService() 存在一些问题:未解决的名称 a 和在条件不为真时缺少返回。我修改了它,以便它可以编译和工作(对我而言)。另外,+1。 - Jonik
5
在Spring 4.2中,不再需要自己编写unwrapFooService。请参见https://dev59.com/6Gsz5IYBdhLWcg3wFT-_#30461953。 - geoand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接