在测试过程中我们会使用大量经过精心设计的测试数据,这些数据展示了测试的目的并且在被测系统中产生影响,我们通过产生的结果来判断软件行为是否符合期望。在敏捷快速迭代交付的背景下,如何快速地设计和产生出“好”的测试数据是一个巨大的挑战。
本文主要总结我对测试数据的理解和使用上的一些实践经验。
测试数据的定义
测试数据是指一组专注于为测试服务的数据,既可以作为功能的输入去验证输出,也可以去触发各类异常场景。测试数据的重要性不言而喻,不全的测试数据意味着有遗落的测试场景,无效的测试数据会增加测试成本,这些问题都会降低软件质量。
毫无疑问,测试数据的理想状态是接近于产品环境的真实数据,所以研究产品环境的数据特点必不可少。在HTSM中产品元素中的数据元素描述了产品数据的大致分类,我们可以按照此分类去研究产品的数据,从而推断出测试数据的内容和特点。
- 输入: 被产品处理的数据
- 输出: 被产品处理后的结果数据
- 预先设置: 产品内建数据或是提供给产品的数据。比如默认值和预先设置好的数据库
- 持久: 被存储在产品内部并且会在多个模块操作中持续存在的数据。比如产品的模式或是状态:选项设置,视角模式等
- 顺序/组合: 被排序或是组合的数据。比如文字顺序和数据排序等
- 基数: 可能会被改变的对象或是字段数。还有像数据库键这样的唯一值
- 大/小: 大小的变化和数据汇总
- 噪音: 无效的、被污染的或是在错误的情况下产生的数据和状态
- 生命周期: 数据生命周期内增查改删的转化
什么样的数据是值得我们选择的,是所谓的“好”数据呢?一般我们要求数据具有下面三个特征
- 现实性 - 数据应该无限接近于产品环境的真实用户数据。比如说地址,我们不能随便写一个不存在的地址,而是选择用户区域的代表性地址;比如说文本,我们不用多个字母乱序随机生成,而是使用类似于lorem ipsum这样的工具去生成。现在很多mock data/fake data工具提供的数据都非常真实。此外,通过对线上用户行为和数据的调研,我们可以总结出一套"Best Candidate Data"或"Golden Data Set",这套数据能代表产品接受和处理最多最典型的数据是什么,我们必须设计场景去覆盖这套数据,也会把这套数据作为回归测试等活动的主要数据。
- 有效性 - 测试数据要符合系统自身的业务逻辑。比如系统不接受大于60岁的年纪,我们不能设计很多大于60岁的数据,因为最终结果都是不会被验证通过。
- 全面性 - 每个测试场景的发生可能会有多个触发条件和结果,我们需要考虑所有适用于场景中不同子场景的数据。
测试数据的设计与产生
在大部分场景中,测试数据是海量的甚至无限的,我们不可能为了测试某一个功能而穷尽所有可能的数据;基于时间、成本和质量的考虑,我们会去抉择哪些数据具有代表性,更加具有找到缺陷的可能性。那么我们会如何设计测试数据?
通常来讲,测试数据的设计是伴随着测试用例的设计。测试用例设计出的测试点或是测试场景需要测试数据来充实,所以下面提到的最基本的测试用例设计方法也适用于测试数据:
- 等价类
- 边界值
- 因果图
- 决策表
- 正向 & 反向
此外,下面方法也是我自己常用的测试数据设计方法
- 数据驱动(data driven) - 大量数据的简单处理
- 组合测试(pairwise testing) - 测试建模
- 探索性测试
- 快递漫游 - 跟着一组数据走遍软件的功能
- 沙发土豆漫游 - 默认值,空值
- 收藏家漫游 - 收集软件功能的输出
- 敌对漫游 - 无效数值
- 测试启发cheatsheet - 对于每一种类型数据的测试启发
测试数据的产生一般是在测试执行之前,产生方法包含:
- 测试人员执行测试过程中手工输入产生
- 来源于测试用例中的测试数据 (designed before test)
- 测试执行时探索式测试启发的数据 (on-the-fly)
- 自动化填表单工具
- SQL脚本 - 批处理更新数据库
- 程序自动生成(Automated Test Data Generation Tools)
- mock data/fake data - 多用于自动化测试(单元测试和API功能测试)
- 自动化脚本创建数据 - 比如说UI自动化测试(效率低),单个某类型数据生成脚本
- 开发过程中创建
- 数据分离 - 为自动化测试服务,项目中提前以数据文件形式储存的数据
- 复制产品环境的数据
- 不过由于安全和隐私的要求,一般来讲,测试数据不能直接复制产品环境
- 通常来讲,因为产品环境数据包含的数据组合数量偏少以及没有无效数据,所以产品环境数据是测试环境数据的子集
- 复制遗留系统的数据
- 文件导入 - 比如说DB的备份或其他格式文件
让我们一起看下不同的测试类型中测试数据的设计和产生有哪些独特方法。
SDLC | 测试类型 | 方法 | 测试数据设计 | 测试数据产生 |
---|---|---|---|---|
Code | 单元测试 | 自动化 | 针对某一个函数方法覆盖更多的代码路径,无效参数 | 使用mock data/fake data自动化产生,从单独的数据文件读取,hard code在程序中(作为参数或变量) |
Test | 功能测试 | 手工/自动化 | 使用基本测试用例设计方法,探索式测试,数据驱动,测试建模,支持最重要的用户场景的测试数据 | 手工输入,使用mock data/fake data自动化产生,从单独的数据文件读取,hard code在程序中(作为参数或变量) |
Test | 性能测试 | 自动化 | 收集产品性能相关信心,设置接近于产品环境的benchmark | 自动化模拟 |
Test | 安全测试 | 自动化 | 验证保密性的数据,验证完整性的数据,身份验证的数据,权限验证的数据 | 从单独的数据文件读取,写在程序中(作为参数或变量) |
专注于test data的测试技术 - Domain Testing
在HTSM的Test Technical提到一个专注于 test data的测试技术
-
域测试 Domain Testing
- 描述: 专注于测试软件所处理的数据 divide and conquer the data
- 典型思路
- 找到产品处理的所有数据。看输出也看输入
- 决定哪些特殊的数据需要测试。考虑边界值、典型值、无效值和最佳代表数据
- 考虑数据的组合
Domain Testing是Functional Testing的一种,通过设计制定特殊的数据作为输入来评估软件的输出,解决了在输入域里无法穷尽测试和选择“最佳”子集数据的问题。通常的手段有:
- 识别关键数据
- 等价类
- 边界值
- 不寻常的数据
- 数据组合
- 触发不同状态的数据
in domain testing, we partition a domain into sub-domains(equivalence classes) and then test using values from each sub-domain
Domain Testing的指导性非常显著,实施Domain Testing可以帮我们形成数据相关的测试思路,比如
- 该变动影响和涉及的测试数据有哪些?
- 数据作为输入变量
- 数据作为输出变量
- 测试数据有什么类型?
- 有哪些无效的数据?如何被系统识别和处理?
- 有哪些有效的数据?不同的有效数据类型如何被系统接收和处理?
- 最重要的测试数据是哪些?考虑边界值、典型值、无效值和最佳代表数据
- 测试数据如何产生?
- 用户产生数据的方式有有哪些?
- 测试数据如何被系统处理?
- 大规模的数据处理
- 同一数据在不同模块之间的处理
- 能否能被成功储存
- 测试数据会在哪些地方以什么方式呈现给用户?
- 测试数据有哪些特点
- 完整性 - 数据在被不同功能或模块容纳处理后依然完整
- 唯一性 - 数据是否在系统里唯一,不能有重复
- 一致性 - 单一数据在系统不同功能或模块中显示一致
- 冲突性 - 不同数据之间是否冲突
测试数据的管理
对于有着不同业务的系统,创造符合业务的特殊数据是需要提前准备的,比如某某国家独特的邮政编码、某个城市具体的街道名称、一个有效的social security number、一个过期的credit card number和一个特殊状态的用户数据等;不然可能会出现在测试执行过程中发现数据不对而去重新等待或寻找数据,造成了时间和精力的浪费。测试数据的管理能够保证测试人员在测试过程中,随时有相应的工具或系统提供所有测试需要的数据,并且这些测试数据会与系统更新同步。测试数据也会因为管理的高效性变得可复用和可追溯。
还有一个要做好测试数据管理的重要原因是由于数据的安全性和隐私性,产品环境的数据是不可能直接拿来使用的,所以通过测试数据的有效管理来持续提供接近于产品环境的anonymized or synthetic data是非常有必要的。
再来总结一下测试数据管理的好处:
- 提供了交付测试数据的统一工具或平台
- 能够实时产生适应需求的测试数据
- 减少了测试数据出错的可能性
- 测试数据可复用
- 测试数据可追溯
- 尽早提供了测试数据,减少不必要的测试等待时间
- 提高测试效率,从而帮助项目高质量交付
总结
测试数据是测试设计中非常重要的一个环节,测试数据准备的好与坏直接影响着项目质量和交付进度。我们既需要使用各类测试技术设计出匹配系统特点的数据,也需要对测试数据进行良好的管理。