【问题标题】:Mocking a property of a CGLIB proxied service not working模拟 CGLIB 代理服务的属性不起作用
【发布时间】:2012-02-20 11:26:05
【问题描述】:

我在尝试从 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 属性的设置器的情况下使其工作。任何帮助将不胜感激。

提前致谢。

【问题讨论】:

    标签: spring mocking cglib proxies


    【解决方案1】:

    简答

    您必须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 不久前谈到了这个陷阱。

    【讨论】:

    • @frandiaz83:很高兴我能帮上忙!考虑accepting 和/或投票正确答案,为未来的读者指出正确的解决方案。
    • 是的,当然。我已经接受了答案,但我不能投票,因为我没有足够的声誉......再次感谢。
    • unwrapFooService() 存在一些问题:未解析名称 a 并且在条件不成立的情况下缺少返回。我冒昧地对其进行了编辑,以便它可以编译和工作(对我而言)。另外,+1
    • Spring 4.2 不再需要自己写unwrapFooService。查看stackoverflow.com/a/30461953/2504224
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-23
    • 2015-12-07
    • 2019-03-09
    相关资源
    最近更新 更多