什么是单元测试?在维基百科中给出的答案是:在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
通常来说,程序员每修改一次程序就会进行最少一次单元测试,在编写程序的过程中前后很可能要进行多次单元测试,以证实程序达到软件规格书要求的工作目标,没有程序错误;虽然单元测试不是必须的,但也不坏,这牵涉到项目管理的政策决定。
单元测试的目标是隔离程序部件并证明这些单个部件是正确的。一个单元测试提供了代码片断需要满足的严密的书面规约。因此,单元测试带来了一些益处。 单元测试在软件开发过程的早期就能发现问题。
常见的单元测试有JUNIT/JUNIT5、AssertJ、Mocktito等,本文主要介绍使用Mocktito做单元测试工具的常用方法
1、Mockito引用的jar包依赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.4.6</version>
<scope>test</scope>
</dependency>
2、Mockito官方地址如下:https://site.mockito.org/
文档详细说明地址如下:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
3、常见命令简介
(1)、@Mock()来创建和注入模拟实例
(2)、@Spy()注释监视现有实例
(3)、@InjectMocks()将模拟字段自动注入到测试对象中,Mockito不支持将模拟注入@Spy程序, 并且以下测试会导致异常,如果我们想让他们俩结合,就需要改造一下,通过构造函数的方法注入
(4)、Verify方法用于检查是否发生了某些行为,使用InOrder可以验证方法的调用顺序
(5)、使用 stubbing 存根模拟连续的调用
(6)、参数匹配,如any(),anyString(),anyInt()等,用来匹配参数信息
(7)、使用 doReturn(),doThrow(),doAnswer(),doNothing(),doCallRealMethod() 来 stub 空方法 void method
4、针对主要方法,对相关测试用例做了汇总,可以查看如下github地址,获取全部测试用例实例,供大家参考:
https://github.com/springk/mockit/tree/master/springk-mockit
5、一些主要代码实例如下所示,实例是通过service层调用数据库层获取用户详细信息,具体代码如下:
a、UserService实例代码如下:
@Service
public class UserService {
/**
* DAO层
*/
@Autowired
private UserDao userDao;
/**
* 根据用户名查询数据库,获取用户信息
* @param name
* @return User
*/
public User getUserInfo(String name){
return userDao.getByName(name);
}
/**
* 根据用户名删除用户信息
* @param name
*/
public void deletByName(String name){
userDao.deleteByName(name);
}
}
b、MockitTest具体实现如下:
@RunWith(MockitoJUnitRunner.class)
public class MockitTest {
/**
* 注入userService对象
*/
@InjectMocks
private UserService userService;
/**
* UserDao模拟数据层调用,创建UserDao实例对象
*/
@Mock
private UserDao userDao;
@Before
public void init(){
//将UserDao实例对象注入到userService中,如果不进行此操作,userService中的userDao对象为null
ReflectionTestUtils.setField(userService, "userDao", userDao);
}
@Test
public void getUserInfoByname(){
//模拟userDao.getByName方法的返回对象,参数为任意String类型
Mockito.when(userDao.getByName(anyString())).thenReturn(getUserInfoInstance());
User user= userService.getUserInfo("mockit");
Assert.assertEquals("mockit-testname",user.getName());
}
private User getUserInfoInstance() {
User user = new User();
user.setName("mockit-testname");
user.setSex("male");
user.setAge(18);
return user;
}
}
以上方法模拟了简单封装数据层的返回实例
6、关于mock异常处理方式如下:
/**
* mockit 抛出异常
* 异常抛出有两种方式:
* 1、方法有返回值的异常抛出
* 2、void方法异常抛出 (抛出的异常要实际的异常类,不能直接抛出基类Exception())
*/
@Test
public void testMockitThrowException(){
//1、方法有返回值的异常抛出
Mockito.when(userDao.getByName(anyString())).thenThrow(new NullPointerException());
User user= userService.getUserInfo("mockit");
//2、void方法异常抛出 (抛出的异常要实际的异常类,不能直接抛出基类new Exception())
doThrow(new RuntimeException()).when(userDao).deleteByName(anyString());
userService.deletByName("mockit");
}
7、对静态方法mock时,使用powermock
a、引用的jar包信息
<!-- 当方法为static时,使用powermock用来完成mock操作 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!-- ######################################## -->
b、具体实现代码如下:
/**
* PowermockTest
* Mockito 不能够 mock 静态方法,可以使用 Powermock。
* PrepareForTest 里面对应的为static方法对应的类名
*/
@RunWith(PowerMockRunner.class )
@PrepareForTest(MockitUtil.class )
public class PowermockTest {
/**
* mock对应静态方法
* 另外一种方式可以自己写一个非静态的方法,对静态方法做个封装
*/
@Test
public void testSomething() {
PowerMockito.mockStatic(MockitUtil.class);
PowerMockito.when(MockitUtil.getMockitName()).thenReturn("test");
Assert.assertEquals(MockitUtil.getMockitName(),"test");
}
}
public class MockitUtil {
/**
* static 方法
* @return Mockit
*/
public static String getMockitName() {
return "Mockit";
}
}