首先,我的问题是:有一长串的数据处理流程,在最后一部分的处理中需要调另一个平台的接口并依赖该接口的返回做剩下的处理,而本地自己单元测试调不到。(不要说将接口返回参数直接写死,这样不太好,虽然我在没有使用 Mockito 时就是这样做的。)既然已经接触了 Mockito 那就用它来解决问题。
我准备了一些有趣的类来完成我的测试:OrderCreate.java,OrderHelper.java,MockSpringTest.java。
package com.practice.mock
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* mock 测试准备类
*
* @author tiger
* @version 1.0.0 createTime: 14/12/27 下午8:57
* @see com.practice.mock.OrderHelper
* @since 1.6
*/
@Component
public class OrderCreate {
@Autowired
private OrderHelper orderHelper;
public void create() {
System.out.println(getAmt());
System.out.println(orderHelper.resolve());
}
public int getAmt() {
return 10;
}
}
package com.practice.mock;
import org.springframework.stereotype.Component;
/**
* mock 测试准备类
*
* @author tiger
* @version 1.0.0 createTime: 14/12/27 下午8:59
* @since 1.6
*/
@Component
public class OrderHelper {
public String resolve() {
return "resolve order";
}
}
package com.practice.mock;
import com.practice.mock.BaseSpringTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.util.ReflectionTestUtils;
import static org.mockito.Mockito.*;
/**
* mock 测试
*
* <p>spinrg + mock 测试的例子
*
* @author tiger
* @version 1.0.0 createTime: 15-1-8 下午5:07
* @since 1.6
*/
public class MockSpringTest extends BaseSpringTest {
@InjectMocks
private OrderCreate orderCreate = mock(OrderCreate.class);
@Mock
private OrderHelper orderHelper;
@Before
public void initMocks() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(11).when(orderCreate).getAmt();
doReturn("success").when(orderHelper).resolve();
doCallRealMethod().when(orderCreate).create();
}
@Test
public void create() {
System.out.println("start mock...");
orderCreate.create();
}
}
输出的结果是:
start mock...
11
success
OrderCreate 的 getAmt 方法和 OrderHelper 的 resolve 方法被成功 mock 掉。
说明一下:@InjectMocks 会给 OrderCreate 装配 orderHelper 属性,其实就是根据 @Mock 来着啊。在下面这句代码执行完后
MockitoAnnotations.initMocks(this);
把被 mock 掉的 OrderHelper 给了 OrderCreate 的私有属性 orderHelper。这里 OrderCreate 本身也是被 mock 掉了。
好,稍微觉得 Mockito 有点意思了吧,但这还没解决我的需求,在我开始 mock 处理时我前面还有一长串的处理。
那我再加几个有趣的类吧:OrderBefore.java,OrderStart.java。
package com.practice.mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* mock 测试准备类
*
* @author tiger
* @version 1.0.0 createTime: 15-1-8 下午7:51
* @since 1.6
*/
@Component
public class OrderBefore {
@Autowired
private OrderStart orderStart;
public void before(){
System.out.println("before...");
orderStart.start();
}
}
package com.practice.mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* mock 测试准备类
*
* @author tiger
* @version 1.0.0 createTime: 15-1-8 下午7:51
* @since 1.6
*/
@Component
public class OrderStart {
@Autowired
private OrderCreate orderCreate;
public void start(){
System.out.println("start...");
orderCreate.create();
}
}
单元测试类 MockSpringTest 的代码变成了这样:
public class MockSpringTest extends BaseSpringTest {
@Autowired
private OrderBefore orderBefore;
@InjectMocks
private OrderCreate orderCreate = mock(OrderCreate.class);
@Mock
private OrderHelper orderHelper;
@Before
public void initMocks() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(11).when(orderCreate).getAmt();
doReturn("success").when(orderHelper).resolve();
doCallRealMethod().when(orderCreate).create();
}
@Test
public void create() {
System.out.println("start mock...");
orderBefore.before();
}
}
猜一下会输出什么。
输出结果:
start mock...
before...
start...
10
resolve order
很遗憾,没有达到预期,分析一下。
从 OrderBefore 的 before() 方法开始,调用了 @Autowired 注入的 orderStart.start(),然后又调了 @Autowired 注入的 orderCreate.create(),再调用了 @Autowired 注入的 orderHelper.resolve() 结束。没错整个流程没有和 mock 扯上关系,似乎唯一的解决办法就是从一开始就 mock,一直 mock 到整个流程结束。但是我前面说了我的流程很长,中间使用 spring @Autowired 注入的类不知道有多少,我就后面几个地方需要 mock,从头 mock 到尾我想都不敢想,那写出来足够吓人的。
幸好,我前面看到了这篇帖子: ,标题和我差不多,不过不影响。
我引进了帖子中的 AopTargetUtils 工具类之后解决了我的问题,MockSpringTest 的代码变成了下面这样:
public class MockSpringTest extends BaseSpringTest {
@Autowired
private Order order;
@Autowired
private OrderBefore orderBefore;
@Autowired
private OrderStart orderStart;
@InjectMocks
private OrderCreate orderCreate = mock(OrderCreate.class);
@Mock
private OrderHelper orderHelper;
@Before
public void initMocks() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(AopTargetUtils.getTarget(orderStart), "orderCreate", orderCreate);
doReturn(11).when(orderCreate).getAmt();
doReturn("success").when(orderHelper).resolve();
doCallRealMethod().when(orderCreate).create();
}
@Test
public void create() {
System.out.println("start mock...");
orderBefore.before();
}
}
成功输出:
start mock...
before...
start...
11
success
另外,BaseSpringTest 是用来加载 Spring 配置,Mock 的常用注解 @Mock,@InjectMocks,@Spy,@Captor 有这些,不了解的话 google 一下吧。