单元测试是对我们写的代码的最小单元进行测试,一般来说就是对函数(方法)进行测试,大家都知道写单元测试的好处,但是具体要怎么写呢?被测试的类可能依赖外部的类或服务,这个依赖的外部接口如何Mock,依赖注入的类如何替换成自己Mock的类等等,下面就介绍一种常用的Mock方式
首先,既然是单元测试,那么要做的第一件事就是屏蔽掉对外部的依赖,不让它们对我们要测试的方法产生影响。屏蔽的方式一般就是通过Mock,将调用的真实方法替换为我们设置好的mock类,这里简单介绍一种Mock框架 Mockito
首先需要引入对应的依赖,如果使用Maven则需要在pom.xml中添加如下内容
1 2 3 4 5 6
| <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.10.19</version> <scope>test</scope> </dependency>
|
创建Mock类
为了进行mock, 第一部我们就需要创建一个mock的类,用来后续替换真实的类
- 通过编程式创建mock类
1 2
| UserService mockUserService = Mockito.mock(UserService.class);
|
- 通过注解方式创建mock类
1 2 3 4
| public class UserTest { @Mock private UserService mockUserService; }
|
但是这种写法需要有一个契机来对注解属性进行mock赋值,这里也有两种方式:
一种是通过在类上面添加@RunWith(MockitoJUnitRunner.class)
注解
还有一种就是通过Junit的@Before注解方法中调用MockitoAnnotations.initMocks(this)
来实现
实现的时候根据情况选择其中一种方式即可
1 2 3 4 5 6 7 8 9 10 11
| @RunWith(MockitoJUnitRunner.class) public class UserTest { @Mock private UserService mockUserService; @Before public void init() { MockitoAnnotations.initMocks(this); } }
|
将Mock类注入到要测试的类中
我们创建mock类,是为了在有依赖的地方可以直接调用mock类,而不是实际的类,所以需要进行替换
假设实际业务代码如下:
1 2 3 4 5 6 7 8 9 10
| @Service public class UserService { private UserDao userDao; public User findUserById(Long id) { return userDao.selectByPrimaryKey(id); } }
|
对于上述场景,可以通过在要测试的类上添加 InjectMocks 注解即可
1 2 3 4 5 6 7 8 9 10 11
| @RunWith(MockitoJUnitRunner.class) public class UserTest {
@InjectMocks private UserService userService;
@Mock private UserDao userDao; }
|
管理mock类的方法
创建并设置好mock类之后,我们来管理mock的方法来实现我们要测试覆盖的场景
使用when…thenReturn… , 来实现调用简单方法时返回特定的值
1 2 3 4
| final User user = new User(); user.setName("Test");
Mockito.when(mockUserService.getUser(Mockito.anyLong())).thenReturn(user);
|
使用 when … thenAnswer 可以实现根据参数值返回不同的内容
1 2 3 4 5 6 7 8 9 10
| Mockito.when(mockUserService.getUser(Mockito.anyLong())).thenAnswer(new Answer<User>() { @Override public User answer(InvocationOnMock invocation) throws Throwable { Long id = invocation.getArgument(0); User user = new User(); return user } });
|
使用verify等验证方法调用次数及参数值
1 2 3 4 5 6 7
| ArgumentCaptor<Long> argumentCaptor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockUserService, Mockito.times(1)).getUser(argumentCaptor.capture());
final Long id = argumentCaptor.getValue(); Assert.assertEquals(1000L, id.longValue());
|
verify需要在实际方法调用之后处理,同时可以配合对应方法的when…then..使用,不会互相影响
也就是说可以通过when…then…对一个方法进行mock返回值
同时还可以使用verify验证参数信息
1 2 3 4 5 6 7 8 9 10 11 12
| final User user = new User(); user.setName("Test"); Mockito.when(mockUserService.getUser(Mockito.anyLong())).thenReturn(user);
User u = mockUserService.getUser(1L); Assert.assertEquals("Test", u.getName());
ArgumentCaptor<Long> argumentCaptor = ArgumentCaptor.forClass(Long.class); Mockito.verify(mockUserService, Mockito.times(1)).getUser(argumentCaptor.capture()); final Long id = argumentCaptor.getValue(); Assert.assertEquals(1L, id.longValue());
|