测试泛谈

1. 前言

软件测试可能是个做开发的都知道,一般的项目都有测试人员做系统测试,至于集成测试和单元测试可能就没多少人写,这个跟公司开发流程和开发人员自身的编码习惯有关,也没什么好与坏之分。

就我个人而言,我是对测试比较感兴趣的,完善的测试开发流程是能够提高团队写代码的效率和保障代码质量的。我自己写代码一般会写单元测试,感觉还是有点效果的,最少提交代码时心里都比较有底气。不过我对测试的了解还是很少,仅仅是知道一些皮毛,所以这次抽空看了敏捷开发相关的开发流程和思想,了解测试驱动的开发和业务驱动开发,受益匪浅。这篇文章就记录一下我对测试的一些理解,当然主要讲跟开发人员比较相贴合的单元测试和集成测试。

2. 软件测试分类及其特征

既然讲测试,就先要了解什么是软件测试,这里引用百度百科的定义:在规定的条件下对程序进行操作,以发现程序错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程。详情看这里:软件测试

软件测试有很多,一般会对对软件测试进行分类,方便理解和记忆。这里引用wiki对软件测试的分类 Software testing

  • 以开发阶段划分
    • 单元测试、集成测试、接口测试、系统测试、验收测试
  • 以代码可见性划分
    • 白盒测试、黑盒测试、灰盒测试、动态测试、静态测试
  • 以测试类型划分
    • 冒烟测试、回归测试、A/B测试、兼容性测试、性能测试、并发测试 一共19种,不再赘述

对于开发人员来说,比较熟悉的测试类别应该是以开发阶段来划分的各个测试,这里做一个比较详细的介绍吧:

单元测试(Unit Testing)

单元测试是对软件组成单元进行测试,其目的是检验软件基本组成单位的正确性。

  • 测试阶段: 编码后
  • 测试对象:函数
  • 测试人员:开发人员
  • 测试方法:白盒测试
  • 测试内容:接收参数的边界值和类型,进行输入时输出是否符合预期,错误处理等

集成测试(Integration Testing)

集成测试也称联合测试、组装测试,将程序模块采用适当的集成策略组装起来,对系统的接口及集成后的功能进行正确性检测的测试工作。主要目的是检查软件单位之间的接口是否正确。

怎么理解呢,这里打个比方,比如在一个Web服务器中,测试控制层(controller)的接口可以看做是一个简单的集成测试,因为在控制层中会使用到服务层、数据持久层等多个方法,达到了将其他模块一起联合测试的效果

  • 测试阶段: 单元测试后
  • 测试对象:模块间的接口
  • 测试人员:开发人员
  • 测试方法:白盒测试和黑盒测试
  • 测试内容:模块间的数据传输、模块组装的功能完整性和正确性、单模块缺陷时对系统的影响等

系统测试(System Testing)

将软件系统看成是一个系统的测试。包括对功能、性能以及软件所运行的软硬件环境进行测试。时间大部分在系统测试执行阶段

比较常见的就是测试人员模拟用户行为在页面、终端上进行操作

  • 测试阶段: 集成测试后
  • 测试对象:整个系统
  • 测试人员:测试人员
  • 测试方法:黑盒测试
  • 测试内容:功能、界面、可靠性、易用性、性能、兼容性、安全性等

验收测试(Acceptance Testing)

验收测试是部署软件之前的最后一个测试操作。它是技术测试的最后一个阶段,也称为交付测试。验收测试的目的是确保软件准备就绪,按照项目合同、任务书、双方约定的验收依据文档,向软件购买都展示该软件系统满足原始需求。

  • 测试阶段:系统测试通过之后
  • 测试对象:整个系统(包括软硬件)。
  • 测试人员:主要是最终用户或者需求方。
  • 测试依据:用户需求、验收标准
  • 测试方法:黑盒测试
  • 测试内容:同系统测试(功能…各类文档等)

3. 系统分层测试

上面讲述了关于测试的分类和详细的介绍了以开发阶段划分的测试。那么,在一个项目中,在不同阶段,不同的模块,不同的层次中使用的测试方法是不一样的,稍后我将以我做的系统做一个分层的例子。

将项目分层,对各层次进行抽象剥离,针对不同层次做不同的测试。每个层次不需要知道其他层次的实现逻辑,只需要关注当前层次业务逻辑,对于其他层次,可以认为其业务逻辑都正确,在此基础上写相关测试。

目前我接触的web项目中,业务都是比较简单的,所以我就简单的对整个系统做一个分层处理,如下:
在这里插入图片描述
项目都是前后端分离的,所以我将它分成了两大模块,前端和服务端,它们之间通过HTTP通信。

因为我是做服务器开发的,前端相关的编码和测试我只是稍微了解过,我个人知道的是前端也有对应的单元测试,针对UI的mock测试以及集成测试等,其他的了解不多,就不再阐述。个人感觉前端UI测试的付出比较高、收益相对较低。

在服务端,因为采用的是传统的MVC架构,所以直接以MVC架构对其分层:

  • 控制层(controller)
  • 服务层(service)
  • 数据持久层(dao)
  • 其他工具工具类和辅助模块

对上述的服务端中,各个层次使用的测试方法可能有所不同:

  • 控制层:单元测试、集成测试
  • 服务层:单元测试
  • 数据持久层:单元测试
  • 其他模块:单元测试

通常工具类可以很方便的使用单元测试进行校验,因为它不依赖其他业务代码,但业务逻辑代码的单元测试就要稍微复杂一些,因为可能会频繁的使用其他模块的功能和方法。

web服务器中可以直接调用API接口作为集成测试,这是相当的方便了。当然,也可以根据不同的需求编写其他的集成测试。

4 单元测试

单元测试,一般而言就只是测试一个函数模块,通过传入不同的参数来测试模块的处理结果是否符合预期。

写单元测试,当要调用其他层次和工具的方法时,要相信它们是正确的。如果数据需要其他层次或工具处理并返回时,可以使用mock技术对使用的方法进行数据模拟处理并返回期望值。

4.1 使用mock技术

mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。

举个例子,假设一个登录的service接口的实现逻辑为如下:

  1. 根据用户id获取用户的用户名和密码
  2. 如果不存在用户,返回错误信息。否则继续
  3. 将密码加密处理
  4. 与查询出来的密码进行对比,返回对比结果

该实现方法如下:


private UserDao userDao;
public Boolean login(Integer uid,String pwd) {

    User u = userDao.selectUserByUid(uid);
    
    if (u == null) {
        return false;   
    }
    
    String pwdEncryStr = EncrypUtil.encry(pwd);
    
    return pwdEncryStr.equals(pwd);
}

使用Spock框架和用Groovy作为测试语言,这里写其中的一个测试用例:

/**
 * 测试用例:用户不存在
 * 测试步骤:
 *       1. mock一个userDao对象
 *       2. 调用userDao.selectUserByUid() 方法时返回 null 值
 *       3. 开始测试,看结果是否符合预期
 */      


@Autowired
LoginService loginService //待测试的类

UserDao userDao = Mock() // mock一个userDao对象

def "user not exists" () {
     given:
       userDao.userDao.selectUserByUid(*_) >> null  //这里设定当调用 selectUserByUid 方法时返回 null
     
    expect:
        loginService.login( 1003 , "password" ) == false // 断言,看是否返回false
}

使用mock技术对方法和对象进行模拟是很方便,但如果一个service方法中需要mock对象的数量很多时,单元测试就变得比较繁琐,需要mock很多对象并且要设定很多返回值,导致写单元测试的收益远远小于不写的情况。有时service层的逻辑复杂时,写测试也是无从下手。如果遇到这种情况,建议检查一下该service接口和各个被使用的方法设计是否合理。

4.2 使用内存数据库

写单元测试时,数据持久层(dao层)的单元测试比其他层次要特殊一点,因为是直接操作数据库的。有人一开始想到的方法是使用一个本地的数据库作为测试数据库,测试数据层接口时直接操作该数据库,测试完成后将数据进行回滚。这方法看起来很不错,但在做测试时依赖了实际的数据库,与实际的开发环境有耦合。如果是团队开发,因每个人的开发环境不同,数据库也不一定存在,这样通常导致测试失败。

其实,在写数据层的单元测试时,可以使用内存数据库作为测试数据。

内存数据库,顾名思义就是将数据放在内存中直接操作的数据库。相对于磁盘,内存的数据读写速度要高出几个数量级,将数据保存在内存中相比从磁盘上访问能够极大地提高应用的性能。

内存数据库是非常适合用於单元测试的,它不依赖实际的数据库,测试代码不管在哪台机器上运行都不会受到限制,而且每一次运行都可以保证数据一致在开发时,通常使用操作数据库的框架进行数据查询的,一般来说,成熟的数据持久层框架都实现了用於单元测试的内存数据库,可以查阅相关文档进行了解。

5. 持续集成

5.1 集成测试

集成测试的概念不再这里重复,这里举一个Web端项目集成测试的例子。

Web服务端的集成测试可以选择api接口作为测试目标,具体测试以下步骤完成:

  1. 根据需求写测试用例
  2. 根据测试用例准备相应的测试数据
  3. 跑所有的测试脚本,将结果保存,如果有错则通知相关人员处理
5.2 持续集成

持续集成是一种软件开发实践,每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。这里大概讲讲它的流程吧。

流程

  1. 提交

流程的第一步,是开发者向代码仓库提交代码。所有后面的步骤都始于本地代码的一次提交(commit)。

  1. 测试

代码仓库对commit操作配置了钩子(hook),只要提交代码或者合并进主干,就会跑自动化测试(主要是单元测试)。

  1. 构建

通过第一轮测试,代码就可以合并进主干,就算可以交付了。交付后可以进行构建了,之后再进行测试

  1. 测试

这次测试是全面测试,主要是单元测试(如果构建前跑完了所有单元测试,这里可以不重复跑)和集成测试,必要时可以加端对端测试。该过程主要是自动化测试,当然有一些自动化测试比较难实现的,就要人工测试。

  1. 部署

全部测试完毕并且通过之后就可以部署到服务器上

持续集成的工具有很多,比如这里就介绍了几种比较流行的持续集成工具:8个流行的持续集成工具,目前我项目中用的是Jenkins,使用感觉还不错,部署方便,推荐使用。

6.总结

写了挺多,因为个人水平有限,所以有很多东西没有细说,还有一些没有选择详细写,说多了显得啰嗦。有需要自己去查找相关资料进行深入了解吧。

本章主要讲了以下几个点:

  • 软件测试的分类
  • 系统的分层测试概念
  • 单元测试的一些技巧
  • 集成测试和持续集成的步骤

如果哪里写的不对可以留言交流交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章