单元测试
单元测试是对软件或者程序的基本(最小)组成单元的测试。单元的测试是可重复执行、执行速度快、独立无依赖、结果不改变。通过写单元测试,我们可以快速验证代码正确性,及早发现程序问题。
测试示例
1 | import org.junit.*; |
常用注解
@Ignore
注解标记的测试方法在测试中会被忽略
@Test
1
2@Test(expected=xxxException.class) 断言该方法会抛出异常
@Test(timeout=1000) 执行时间超过设置的值该案例会失败@RunWith
1
2
3
4@RunWith(Suite.class) 测试集运行器配合使用测试集功能
@RunWith(JUnit4.class) 默认运行器
@RunWith(Parameterized.class) 参数化运行器
@RunWith(Suite.class)@Mock和@Spy
断言
1 | assertEquals(a, b) 测试a是否等于b(a和b是原始类型数值(primitive value)或者必须为实现比较而具有equal方法) |
Mock
mock就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。这个虚拟的对象就是mock对象,mock对象就是真实对象在调试期间的代替品。
mockito
mock对象使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//通过api创建
public class UserServiceTest {
private UserService userService;
private UserDao mockUserDao;
public void setUp() {
mockUserDao = mock(UserDao.class);
userService = new UserServiceImpl();
userService.setUserDao(mockUserDao);
}
//通过注解
public class UserServiceTest {
private UserServiceImpl userService;
private UserDao mockUserDao;
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}Mock常用方法
verify
1
2
3
4verify(mock, never()).add("twice"); //验证add方法没有被调用
verify(mock, times(2)).add("twice"); //验证add方法被调用了2次
verify(mock, atLeast(n)).someMethod(); //方法至少被调用n次
verify(mock, atMost(n)).someMethod(); //方法最多被调用n次when
1
2
3
4when(mock.someMethod()).thenReturn(value1).thenReturn(value2); //设置方法返回值,第一次返回value1,第二次返回value2
when(mock.get(0)).thenReturn("first");
when(mock.get(1)).thenThrow(new RuntimeException());
when(mock.get(anyInt())).thenReturn("element");spy
1
2
3List spy = spy(new LinkedList());
when(spy.get(0)).thenReturn(“foo");
doReturn("foo").when(spy).get(0);
Mock VS Spy
Mock需要制定mock类型类(接口或者实现类),生成Mock类,其中所有的方法都不是真实的方法,而且返回值都是Null
1
2
3
4
5
6
7
8
9
10public class Service {
public String service() {
System.out.print("invoked!");
return "real service!";
}
}
Service mock = Mockito.mock(Service.class);
System.out.println(mock.service()); //null
when(mock.service()).thenReturn("mock");
System.out.println(mock.service()); //mockSpy 机制可以监视一个真实的对象,这时对它进行方法调用将执行真实的方法,同时也可以打桩指定的方法。总结来说, Spy机制提供了部分mock的能力。
1
2Service spy = Mockito.spy(new Service());
System.out.println(mock.service()); //inovked!real service!
powermock
使用powermock可以通过修改字节码的方式模拟final/static/private/系统类的功能。
mock new关键字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25Class LocalServiceImpl implements LocalService{
public Node getLocalNode(int num, String name) {
return new Node(num, name);
}
/**
* mock new关键字
*/
//PrepareForTest修改local类的字节码以覆盖new的功能 (LocalServiceImpl.class)
public void testNew() throws Exception {
Node target = new Node(1, "name");
//当传入任意int且name属性为"name"时,new对象返回为target
//当参数条件使用了any系列方法时,剩余的参数都得使用相应的模糊匹配规则,如eq("name")代表参数等于"name"
//剩余还有isNull(), isNotNull(), isA()等方法
PowerMockito.whenNew(Node.class).withArguments(anyInt(), eq("name")).thenReturn(target);
Node result = localService.getLocalNode(2, "name");
assertEquals(target, result); //返回值为target
assertEquals(1, result.getNum());
assertEquals("name", result.getName());
//未指定name为"test"的返回值,默认返回null
Node result2 = localService.getLocalNode(1, "test");
assertNull(result2);
}
}Mock final方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//RemoteServiceImpl
public final Node getFinalNode() {
return new Node(1, "final node");
}
/**
* mock final方法
*/
//final方法在RemoteServiceImpl类中 (RemoteServiceImpl.class)
public void testFinal() {
Node target = new Node(2, "mock");
PowerMockito.when(remoteService.getFinalNode()).thenReturn(target); //指定返回值
Node result = remoteService.getFinalNode(); //直接调用final方法,返回mock后的值
assertEquals(target, result); //验证返回值
assertEquals(2, result.getNum());
assertEquals("mock", result.getName());
}mock static方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//Node
public static Node getStaticNode() {
return new Node(1, "static node");
}
/**
* mock static方法
*/
//static方法定义在Node类中 (Node.class)
public void testStatic() {
Node target = new Node(2, "mock");
PowerMockito.mockStatic(Node.class); //mock static方法前需要加这一句
PowerMockito.when(Node.getStaticNode()).thenReturn(target); //指定返回值
Node result = Node.getStaticNode(); //直接调用static方法,返回mock后的值
assertEquals(target, result); //验证返回值
assertEquals(2, result.getNum());
assertEquals("mock", result.getName());
}Mock private方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28//RemoteServiceImpl
public Node getPrivateNode() {
return privateMethod();
}
//RemoteServiceImpl
private Node privateMethod() {
return new Node(1, "private node");
}
/**
* mock 私有方法
*/
//private方法定义在RemoteServiceImpl类中 (RemoteServiceImpl.class)
public void testPrivate() throws Exception {
Node target = new Node(2, "mock");
//按照真实代码调用privateMethod方法
PowerMockito.when(remoteService.getPrivateNode()).thenCallRealMethod();
//私有方法无法访问,类似反射传递方法名和参数,此处无参数故未传
PowerMockito.when(remoteService, "privateMethod").thenReturn(target);
Node result = remoteService.getPrivateNode();
assertEquals(target, result); //验证返回值
assertEquals(2, result.getNum());
assertEquals("mock", result.getName());
}Mock系统方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//RemoteServiceImpl
public Node getSystemPropertyNode() {
return new Node(System.getProperty("abc"));
}
/**
* mock 系统类方法
*/
//类似new关键字,系统类方法的调用在类RemoteServiceImpl中,所以这里填的是RemoteServiceImpl (RemoteServiceImpl.class)
public void testSystem() {
PowerMockito.mockStatic(System.class); //调用的是系统类的静态方法,所以要加这一句
PowerMockito.when(System.getProperty("abc")).thenReturn("mock"); //设置System.getProperty("abc")返回"mock"
PowerMockito.when(remoteService.getSystemPropertyNode()).thenCallRealMethod(); //设置mock对象调用实际方法
Node result = remoteService.getSystemPropertyNode(); //按代码会返回一个name属性为"mock"的对象
assertEquals(0, result.getNum()); //int默认值为0
assertEquals("mock", result.getName()); //remoteService对象中调用System.getProperty("abc")返回的是上面设置的"mock"
}