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

使用Mockito和junit进行单测的一些要点:
1,总的来说写一个单测需要提前进行三件事
a,看好你要测的函数的入参,构建出一个入参;
b,详细看好你的函数中依赖了哪些写好的函数,这些函数需要进行
@Mock声明,将他们mock掉,使得你的单测只测试你写的逻辑代码;

c,判断你要得到什么样的结果,也就是你的函数要改变哪些变量的值,然后在UT的最后用Assert断言来对这些期望值进行预测判断

下面写一个例子,本例要验证fillModel这个函数,它的作用是将一个List中每一个model的defined属性设置为true,需要调用外部的依赖itemService来获取一个model2的defined值,然后将这个值填入model的defined属性中。

本例中输入参数是一个List,一个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 models = Lists.newArrayList(model);

        //做好预期的结果
        Map 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()).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的使用相对简单,但是有很多小细节需要注意,以后使用过程中遇到的问题会更在后面。



你可能感兴趣的:(使用Junit和mockito写单测的一些注意点)