Mockito官方文档
PowerMockito可以mock private、final、static等方法。
@InjectMocks
Service service;
@Mock
Person person;
@Mock创建一个mock service,当你需要测试的类/方法中有依赖的类,可以将依赖的类mock掉,底层原理是通过代理的方式。比如Service类中的功能依赖了Person类,那么可以将Person类mock掉。该方式可以获得Person类包含的所有方法,而通过mock(person)调用其任何方法一般都返回null、空或0值,他不会调用真实方法。
@InjectMocks创建一个实例对象,他是可以调用真实的方法的,通过service调用其方法是真实的。而@Mock(@Spy)创建的mock会被注入到该实例中。
一般推荐使用@Mock,但是总是会出现一个类,你想要mock掉一部分方法,但是有另一部分你希望是可以真实调用的,这时你可以使用@Spy
Person p = PowerMockito.spy(new Person());
此时,你想要在spy对象(p)上打桩,不能够使用when(),而是doReturn()|doThrow()|doAnswer()|doNothing()|doCallRealMethod()家族。如mockito文档中举的例子:
List list = new LinkedList();
List spy = spy(list);
//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");
//You have to use doReturn() for stubbing:
doReturn("foo").when(spy).get(0);
public class Check{
private final A a;
private final B b;
public Check(A a,B b){
this.a = a;
this.b = b;
}
//保证SimpleDateFormat的线程安全性
private static final ThreadLocal DATE_FORMAT_YYYYMMDD = ThreadLocal.withInitial(()->new SimpleDateFormat("yyyyMMdd"));
private static final ThreadLocal DATE_FORMAT_YYYYMM = ThreadLocal.withInitial(()->new SimpleDateFormat("yyyyMM"));
public void sink(Record record){
if(record == null){
log.info("record:检核结果为null");
}
try{
String yyyyMM;
String yyyyMMdd;
if(null != record.getProcessTime()){
yyyyMM = DATE_FORMAT_YYYYMM.get().format(record.getProcessTime());
yyyyMMdd = DATE_FORMAT_YYYYMM.get().format(record.getProcessTime());
}else{
log.info("recon:统计时间为空");
return;
}
if(StringUtils.isEmpty(yyyyMM)||StringUtils.isEmpty(yyyyMMdd)){
log.info("record统计时间出错:{}",record.getProcessTime());
return;
}
if(!updateKey(record,yyyyMMdd){
return;
}
Response response = saveObject(record,yyyyMM);
log.info("存储{}到数据库成功",response);
}catch(Exception e){
log.erro("sink异常",e);
}
}
private boolean updateKey(Record record,String s){
.....
}
private Response saveObject(Record record,String s){
......
}
}
@RunWtih(PowerMockRunner.class)
public class CheckTest{
@InjectMocks
Check check;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
@Test
public void sink_recoedIsNull(){
Record record = PowerMockito.spy(new Recod());
check.sink(null);
//因为需要在record判断其方法procesTime是否被调用,如果使用mock,则会导致没有调用processTime,那么这个verify(never次)永远成立,所以,这里要使用spy
Mockito.verify(record,Mockito.never()).getProcessTime();
}
}
2. 测试record的字段processTime字段为null
如果processTime为空,那么StringUtils不会调用isEmpty方法。是对StringUtils的静态方法的mock
@Test
@PreparedForTest(StringUtils.class)
public void sink_ProcessTimeIsNull(){
Record record = new Record();
PowerMockito.mockStatic(StringUtils.class);
check.sink(record);
PowerMockito.verifyStatic(StringUtils.class,Mockito.never());
StringUtils.isEmpty(anyString());
}
3. 测试yyyyMM, yyyyMMdd时间转换出错
PowerMockito.verifyPrivate(mock,.....),需要一个mock对象,但是又要验证对check的私有方法进行验证,所以这里用了spy对象
@Test
@PreparedForTest(value={StringUtils.class,Check.class})
public void sink_ProcessTimeWrong(){
Record record = new Record();
record.setProcessTime(new Date());
PowerMockito.mockStatic(StringUtils.class);
PowerMockito.when(StringUtils.isEmpty(anyString())).thenReturn(true);
A a = PowerMockito.mock(A.class);
B b = PowerMOckito.mock(B.class);
Check check1 = PowerMockito.spy(new Check(a,b));
check.sink(record);
try{
PowerMockito.verifyPrivate(check1,Mockito.never()).invoke("updateKey",any(Record.class),anyString());
}catch (Exception e){
e.printStackTrace();
}
}
4. 测试更新成功情况
@Test
@PreparedForTest(value={StringUtils.class,Check.class})
public void sink_KeyUpdateSuccess(){
Record record = new Record();
record.setProcessTime(new Date());
PowerMockito.mockStatic(StringUtils.class);
PowerMockito.when(StringUtils.isEmpty(anyString())).thenReturn(false);
A a = PowerMockito.mock(A.class);
B b = PowerMOckito.mock(B.class);
Check check1 = PowerMockito.spy(new Check(a,b));
try{
//必须使用doReturn
PowerMockito.doReturn(true).when(check1,"updateKey",any(Record.class),anyString());
check.sink(record);
PowerMockito.verifyPrivate(check1,Mockito.times(1)).invoke("saveObject",any(Record.class),anyString());
}catch (Exception e){
e.printStackTrace();
}
}