Mockito InjectMocks Issue when integrated with Spring Annotation

Assuming we have the following Service to write Unit Test.

public interface ITestService {

    @Transactional
    void test(String id);

}


@Service
public class TestServiceImpl implements ITestService {
    @Autowired
    private ITestDao testDao;

    @Autowired
    private IOtherService otherService;

    @Override
    public void test(String id) {
        // business logic here
    }

}

We want to test TestServiceImpl, but we reference the IOtherService in TestServiceImpl. Regarding to the Unit Test guideline, we should assuming the IOtherService is always working correctly. So we can use Mockito to mock the IOtherService. The code is as following.
In our test case, we want to mock the IOtherService, but as for the ITestDao, we want to use the actual object. So we use @Mock on IOtherService, @Autowired on ITestDao.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application-context-test.xml")
@WebAppConfiguration("src/test/webapp")
public class BpRelationshipServiceTest {

    @Mock
    IOtherService otherService;

    @Autowired
    @InjectMocks
    ITestService testService;

    @Autowired
    private ITestDao testDao;

    @Test
    public void testCURD(){
        // test cases here
    }

    @Before
    public void setUp() throws Exception{
        MockitoAnnotations.initMocks(this);
        when(otherService.someMethod()).thenReturn(value);
    }

    @After
    public void tearUp() {
        // recover the data
    }
}

But due to the @Transactional annotation on ITestService, Spring will generate a proxy for the TestServiceImpl which the properties are all autowired by Spring. So when we use Mockito @InjectMocks, the injection of Mock Object won’t work as expected.
And the solution is, after MockitoAnnotations.initMocks(this), we get the TargetObject from proxy using Reflection Mechanism, and set the IOtherService on the TargetObject. Here is the code.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application-context-test.xml")
@WebAppConfiguration("src/test/webapp")
public class BpRelationshipServiceTest {

    @Mock
    IOtherService otherService;

    @Autowired
    @InjectMocks
    ITestService testService;

    @Autowired
    private ITestDao testDao;

    @Test
    public void testCURD(){
        // test cases here
    }

    @Before
    public void setUp() throws Exception{
        MockitoAnnotations.initMocks(this);
        ReflectionTestUtils.setField(AopTargetUtils.getTarget(testService), "otherService", otherService);
        when(otherService.someMethod()).thenReturn(value);
    }

    @After
    public void tearUp() {
        // recover the data
    }
}

public class AopTargetUtils {

    /**
     * 获取 目标对象
     * 
     * @param proxy
     *            代理对象
     * @return
     * @throws Exception
     */
    public static Object getTarget(Object proxy) throws Exception {
        if (!AopUtils.isAopProxy(proxy)) {
            return proxy;// 不是代理对象
        }
        if (AopUtils.isJdkDynamicProxy(proxy)) {
            return getJdkDynamicProxyTargetObject(proxy);
        } else { // cglib
            return getCglibProxyTargetObject(proxy);
        }
    }

    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object dynamicAdvisedInterceptor = h.get(proxy);
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
        return target;
    }

    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        AopProxy aopProxy = (AopProxy) h.get(proxy);
        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
        return target;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值