什么是单元测试
摘自维基百科的解释:单元测试
在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
可以看到,对于Java这种面向对象的语言来说,单元测试就是对一个类的行为进行测试,通俗来说,每个方法都有入参和返回值,输入不同的参数会得到不同的返回值,或者异常;那么单元测试就是根据不同的use case来模拟不同场景执行,验证我们的代码在不同的执行路径下,是否会返回预期的结果。
mockito
在日常工作中,我们基本都会基于spring这种容器框架来做开发,并且大部分情况下,项目都会依赖外部系统,比如DB、分布式缓存、第三方接口等。那么我们在针对某个类进行单元测试的时候,就会遇到各种困难:操作DB造成脏数据、第三方接口测试环境不可达等等。这时候,我们就需要一种方式屏蔽这些依赖组件对测试的影响,mockito就是这样一种mock工具,帮我们解决上述这些问题,当然类似的工具还有easymock、powermock、基于groovy的Spock等。
举个例子
话不多数,上代码,假设我们有一个基于spring-boot的web项目,代码结构如下:
我们针对业务层(biz) UserBiz类生成一个单元测试类,使用Junit4作为单元测试框架,mockito作为mock框架:
@Component
public class UserBiz {
@Autowired
private UserDAO userDAO;
public User getUserById(String id){
if (id == null || id.isEmpty()){
throw new BizException("param:id cant not be null or empty");
}
return userDAO.getUserByPrimaryKey(id);
}
}
- 可以看到,里边依赖了DAO层的代码,正常运行时要加载整个spring的上下文才能完成依赖注入,由于DAO层依赖数据库,还要加载数据源等一系列动作,此时我们可以选择mock掉对userDAO的依赖,只测试UserBiz的行为;
- mockito的语法很简单,特别是结合注解的方式,其中:
- @InjectMock就是需要测试的类
- @Mock就是用来注解被mock的类
- when() thenReturn()等方法结合,可以让我们对被mock对象的行为做一些控制,简单来说就是你可以控制它在输入某种参数的时候,能够返回你你期望的结果,来满足你对测试对象的行为路径控制
@RunWith(MockitoJUnitRunner.class)
public class UserBizTest {
@InjectMocks
private UserBiz userBiz;
@Mock
private UserDAO mock;
@Test(expected = BizException.class)
public void getUserById_throwException_whenIdIsNull(){
userBiz.getUserById(null);
fail("不应该走到这里");
}
@Test
public void getUserById_returnNormal(){
User user = new User();
when(mock.getUserByPrimaryKey(any(String.class))).thenReturn(user);
User userById = userBiz.getUserById("1");
Assert.assertNotNull(userById);
verify(mock, times(1)).getUserByPrimaryKey(any());
}
}
单元测试的意义
- 提高代码质量,从而提高产品质量
- 降低集成测试、回归测试成本,从而提高软件测试效率
- 描述业务,有利于业务传承