单元测试笔记

单元测试 笔记

学习链接
Mockito官方文档
极客学院
TestNg
Mockito中文教程

Mockito 使用情景

我们往往会遇到要测试的类有很多依赖,这些依赖的类/对象/资源又有别的依赖,从而形成一个大的依赖树,要在单元测试的环境中完整地构建这样的依赖,是一件很困难的事情。

基本使用

1
2
3
4
5
6
7
8
9
10
@Test
public void test() {
ObjectB objectB = mock(ObjectB.class);
ObjectA objectA = new ObjectA();
objectA.setPwString("testString");
when(objectB.getObjectA()).thenReturn(objectA);

ObjectA actucalA = objectB.getObjectA();
assertEquals(objectA, actucalA);
}

注意使用注释快速Mock的时候需要MockitoAnnotations.initMocks(this);参考2.5

参数匹配

在匹配方法的参数时候可以写准确值也可以使用any()

1
2
3
4
5
6
7
when(templateDAO.findAll(any(TemplateSpecification.class))).thenThrow(new 
DataAccessException("query template failed") {
private static final long serialVersionUID = -6784264998739312988L;
});
when(templateDAO.findAll(any(TemplateSpecification.class))).thenThrow(new
DataAccessException("query template failed") {
});

  • 这里需要注意的是一旦参数使用了一个matcher,那么后面的参数都必须matcher
  • 可以自己自定义参数匹配

doReturn().when()和when().doReturn()区别

这里需要注意一下when().doReturndoReturn().when()的区别

  • doReturn().when()不会真的去调用该方法
  • when().doReturn()在监测对象是完全mock对象的时候不会调用方法(因为mock就是一个假对象),而当对象是spy的时候会真的去调用该方法
  • 当调用方法返回值是空的时候,只能用doReturn().when().method()

验证异常

代码验证

1
2
3
4
5
6
7
@Rule
public ExpectedException expectedEx = ExpectedException.none();
//...
expectedEx.expect(CannotUploadTemplateException.class);
expectedEx.expectMessage("Template Rejected");
expectedEx.expect(CannotUploadTemplateException.class);
expectedEx.expectMessage("Template Rejected");

使用注释来验证异常

1
2
3
4
5
6
@Test(expected = RuntimeException.class)
public void doThrow_when(){
List list = mock(List.class);
doThrow(new RuntimeException()).when(list).add(1);
list.add(1);
}

Spy检测对象

spy是一个可以检测实际对象的技术,能够监测方法并设置对象行为,这里就需要注意之前所说的doReturn().when()when().doReturn()的区别,理解起来就是一种可实可虚的半mock对象

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test() {
ObjectB objectB = spy(new ObjectB());
ObjectA objectA = new ObjectA();
objectA.setPwString("testString");
when(objectB.getObjectA()).thenReturn(objectA);

ObjectA actucalA = objectB.getObjectA();
assertEquals(objectA, actucalA);
//这里getValue()会调用实际的方法,返回值是"hello world"
assertEquals("hello world", objectB.getValue());
}

重置Mock

1
2
3
4
5
6
7
8
9
10
 @Test
public void reset_mock(){
List list = mock(List.class);
when(list.size()).thenReturn(10);
list.add(1);
assertEquals(10,list.size());
//重置mock,清除所有的互动和预设
reset(list);
assertEquals(0,list.size());
}

连续调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test(expected = RuntimeException.class)
public void consecutive_calls(){
//模拟连续调用返回期望值,如果分开,则只有最后一个有效
when(mockList.get(0)).thenReturn(0);
when(mockList.get(0)).thenReturn(1);
when(mockList.get(0)).thenReturn(2);
when(mockList.get(1)).thenReturn(0).thenReturn(1).thenThrow(new RuntimeException());
assertEquals(2,mockList.get(0));
assertEquals(2,mockList.get(0));
assertEquals(0,mockList.get(1));
assertEquals(1,mockList.get(1));
//第三次或更多调用都会抛出异常
mockList.get(1);
}

验证执行顺序

详情参考2.16

确保模拟对象上无互动发生

详情参考2.17

找出冗余的互动(即未被验证到的)

详情参考2.18