您的位置:首页 > 其它

使用Junit和mockito写单测的一些注意点

2018-03-10 15:22 381 查看
使用Mockito和junit进行单测的一些要点:1,总的来说写一个单测需要提前进行三件事a,看好你要测的函数的入参,构建出一个入参;b,详细看好你的函数中依赖了哪些写好的函数,这些函数需要进行@Mock声明,将他们mock掉,使得你的单测只测试你写的逻辑代码;c,判断你要得到什么样的结果,也就是你的函数要改变哪些变量的值,然后在UT的最后用Assert断言来对这些期望值进行预测判断下面写一个例子,本例要验证fillModel这个函数,它的作用是将一个List<Models>中每一个model的defined属性设置为true,需要调用外部的依赖itemService来获取一个model2的defined值,然后将这个值填入model的defined属性中。本例中输入参数是一个List<Models>,一个model里填入kdtId和id两个属性,所以首先进行参数准备(见代码);本例需要依赖itemService下的getSpuMap方法,希望这个方法返回一个map,而这个map中value元素model2的defined属性经过这个方法被设置为true,注意这个方法不是我们写的,所以在这里需要被mock掉,而mock掉后返回的结果是希望含有defined属性为true的。所以,首先我们构造一个这个itemService方法的返回值,也就是一个map,这个map的value是一个model2类,而model2的defined被我们预先设置为true;然后用when语句mock掉itemService方法,使其返回我们构造好的这个map:when(itemService.getSpuMap(anyLong(), anyList())).thenReturn(map);最后调用我们要测验的方法,然后查看调用后model的defined是否和我们预设的model2的值一样。需要注意的是,因为我们要测试的fillModel这个方法是需要被实际执行的,不能被mock,所以这个方法的类(通常也就是你的测试类对应的方法类)需要加上@InjectMock注解。而其中依赖的itemService.getSpuMap方法不是我们写的,我们只是依赖于它的返回值,这个类的初始化要加上@Mock注解。
@Test
public void test_fillModel(){

//参数准备
Long kdtId = 100L;
Long id = 10002L;
Model model = new Model();
model.setKdtId(kdtId);
model.setId(id);
List<Model> models = Lists.newArrayList(model);

//做好预期的结果
Map<Long, Model2> map = new HashMap<>();
Model2 model2 = new Model2();
model2.setItemId(id);
model2.setKdtId(kdtId);
model2.setDefined(true);
spuMap.put(10002L,model2);

//通过when语句mock出fillHasMultiSku函数中所依赖的getSpuMap资源,该资源输入任意参数,得到之前做好的预期结果spyMap
when(itemService.getSpuMap(anyLong(), anyList())).thenReturn(map);
//实际执行fillHasMultiSku函数,models中填入信息
itemListInnerService.fillModel(models);

Assert.assertEquals(models.get(0).isDefined(),itemSkuTotalModel.isDefined());
}
另外还有很多实用Mockito进行测试的小问题,举几个例子:
1,巧用verify语句
verify是用来验证某函数的执行与否,执行几次,没有被执行等 @Test
public void verifying_number_of_invocations(){
List list = mock(List.class);
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(3);
list.add(3);
//验证是否被调用一次,等效于下面的times(1)
verify(list).add(1);
verify(list,times(1)).add(1);
//验证是否被调用2次
verify(list,times(2)).add(2);
//验证是否被调用3次
verify(list,times(3)).add(3);
//验证是否从未被调用过
verify(list,never
9966
()).add(4);
//验证至少调用一次
verify(list,atLeastOnce()).add(1);
//验证至少调用2次
verify(list,atLeast(2)).add(2);
//验证至多调用3次
verify(list,atMost(3)).add(3);2,用doThrow验证抛出异常@Test(expected = RuntimeException.class)
public void doThrow_when(){
List list = mock(List.class);
doThrow(new RuntimeException()).when(list).add(1);
list.add(1);
} 3,用spy来真正调用真实的api@Test
public void real_partial_mock(){
//通过spy来调用真实的api
List list = spy(new ArrayList());
assertEquals(0,list.size());
A a = mock(A.class);
//通过thenCallRealMethod来调用真实的api
when(a.doSomething(anyInt())).thenCallRealMethod();
assertEquals(999,a.doSomething(999));
}

class A{
public int doSomething(int i){
return i;
}
} 4,使用 new Answer()来对未预设的调用更改默认期望值@Test
public void unstubbed_invocations(){
//mock对象使用Answer来对未预设的调用返回默认期望值
List mock = mock(List.class,new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return 999;
}
});
//下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值
assertEquals(999, mock.get(1));
//下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值
assertEquals(999,mock.size());
} Mock的使用相对简单,但是有很多小细节需要注意,以后使用过程中遇到的问题会更在后面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: