不期速成 日拱一卒 不期速成 日拱一卒
首页
技术
测试
分类
标签
归档
关于
首页
技术
测试
分类
标签
归档
关于
  • 基础

  • 进阶

    • Wady(绿洲)Java SDK单元测试从0到1的突破
      • 1 Java SDK单元测试
        • 1.1 什么是SDK
        • 1.2 什么是Wady
      • 2 Wady具备哪些特性
        • 2.1 新特性
        • 2.2 即将支持的特性
      • 3 更深入的了解Wady特性
        • 3.1 关于单元测试的数据驱动配置
        • 3.2 关于单元测试的用例代码
      • 4 致谢
    • Wady在SDK测试中的应用
    • 组合测试
  • 探索

  • 把你的测试用例当作一幅画(邰晓梅)
  • 测试
  • 进阶
姜越
2020-12-22

Wady(绿洲)Java SDK单元测试从0到1的突破

From 智能化产品测试团队

# 1 Java SDK单元测试

智能化算法产品团队于2020年初,正式承接智能算法Java SDK的测试,拉开了我们在白盒测试领域探索与实践的序幕。

经过选型测试框架、规范测试流程、打通测试环境等方面的不断落地,使得我们在上半年快速具备了Java SDK的基本测试能力,能够满足SDK接口的功能、性能、以及围绕算法效果的测试。

在今年九月份,我们通过对TestNG框架进行二次开发,重点增强了其数据驱动能力,使得SDK单元测试代码编写成本降低50%,同时通过设计实现代码与数据的解耦,提升了用例的可维护性。

至此,Wady(绿洲)Java SDK 单元测试框架正式成型(如下图,黄色框部分),并投入到实际测试当中。

image-20201223152538512

这里,我们将更多的从技术角度,介绍Wady的在单元测试中的设计优化与实际应用。

# 1.1 什么是SDK

SDK即“软件开发工具包”,一般是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。通俗点是指由第三方服务商提供的实现软件产品某项功能的工具包。

通常SDK是由专业性质的公司提供专业服务的集合,比如提供安卓开发工具、或者基于硬件开发的服务等。也有针对某项软件功能的SDK,如推送技术、图像识别技术、移动支付技术、语音识别分析技术等。开发者不需要再对产品的每个功能进行开发,选择合适稳定的SDK服务并花费很少的精力就可以在产品中集成某项功能。

# 1.2 什么是Wady

智能化算法测试团队基于Java SDK测试场景提供的统一单元测试框架—--—Wady,其一定程度解决以下几类问题:

  • 测试数据与代码耦合程度较高,用例的可扩展性和可复用性较低。
  • 测试覆盖度的提高与测试用例维护成本增长之间的线性关系。
  • 缺乏灵活统一的期望比对方式,存在较高的用例开发成本(重复度高)。

# 2 Wady具备哪些特性

# 2.1 新特性

  • 数据驱动,基于Yaml 实现测试用例与测试数据的解耦。

  • 参数化,支持指定参数项的参数化,实现参数的排列组合、顺序组合,为低维护成本下实现高覆盖提供可能,除了能够实现入参的参数化,也支持了对期望结果的参数设置。

  • 提供统一且唯一的数据驱动方法,支持根据测试类、测试方法在指定目录下自动加载对应的配置文件。

  • 提供统一的测试用例入参格式,全局唯一入参格式。

  • 提供统一灵活的结果比对方法,支持JsonObject、JsonArray、String、Integer等基本数据类型的精确、模糊比较。

# 2.2 即将支持的特性

  • 接口测试用例自动生成,通过解析Jar文件,自动生成各接口的测试代码,实现对测试代码依赖解耦,将代码开发、维护成本降至到零。
  • Yaml配置文件自动生成,根据接口方法入参,生成测试数据配置文件,实现驱动测试。

# 3 更深入的了解Wady特性

# 3.1 关于单元测试的数据驱动配置

关于数据驱动配置,一图抵千言,如下:

IMG_3554

# 3.1.1 数据驱动特性

YAML语言的设计参考了JSON、XML和SDL等语言。YAML 强调以数据为中心,简洁易读,编写简单,YAML基本格式要求,如下:

  • 大小写敏感
  • 通过缩进表示层级关系
  • 禁止使用tab缩进,只能使用空格键
  • 缩进的空格数目不重要,只要相同层级左对齐即可
  • 使用#表示注释

借助YAML多文档块特性强化TestNG数据驱动

在对某些方法进行测试时,我们通常会使用不同的数据对方法进行覆盖,如 边界值测试、等价类测试,YAML多文档块(---)实现了在一个yaml中,隔离不同测试数据的目的,即可通过一个YAML文件的多个配置块驱动一个测试方法执行。

---
# 用例描述
testcase: 验证 XXX 功能是否符合预期
# 参数配置
parameter:
  jsonObjecta: {"Id":"1","code":"Connect","name":"连接","sentenceDesc":"","type":"DEVICE","grade":[1,2,3,4]}
# 期望配置
expectResult:
  expect: {"Id":"2","code":"Connect","name":"连接","sentenceDesc":"","type":"DEVICE","grade":[1,2,3,4]}

---
# 用例描述
testcase: 验证 XX 功能是否符合预期
# 参数配置
parameter:
  jsonObjecta: {"Id":"3","code":"Connect","name":"连接","sentenceDesc":"","type":"DEVICE","grade":[1,2,3,4]}
# 期望配置
expectResult:
  expect: {"Id":"3","code":"Connect","name":"连接","sentenceDesc":"","type":"DEVICE","grade":[1,2,3,4]}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 3.1.2 数据驱动引入参数化

参数化是对数据驱动的进一步加强,帮助我们最大程度的减少数据配置成本,如我们需要进行参数组合覆盖时,需要通过文档块逐一编写测试数据配置,重复性很高,随着配置的不断增多,维护成本也剧增。

参数化特性实现了参数灵活的排列组合和顺序排列。

# 3.1.2.1 排列组合
{"Id":"244","code":"Connect","sentenceDesc":{"desc":"","sentence":"你好"},"grade":[1,2,3,4]}
1

如,我们根据测试用例覆盖需要对以上参数中的desc、grade中的第0个元素,进行不同参数组合的覆盖,通过在配置文件中添加parameterization配置项即可,如下:

---
testcase: 验证 XX 功能是否符合预期
# 参数化配置
parameterization:
  jsonObjectDemo.sentenceDesc.desc: 数据驱动, 参数化
  jsonObjectDemo.grade[0]: 99,88,77

# 参数组合方式,非必填,支持顺序和排列组合,默认排列组合
parameterCombination: permutation

# 参数配置
parameter:
  jsonObjectDemo: {"Id":"244","code":"Connect","sentenceDesc":{"desc":"","sentence":"你好"},"grade":[59, 33]}

# 期望配置
expectResult:
  expect: {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

如上参数化配置,当我们选择排列组合(parameterCombination: permutation)时,用例运行时,将数据配置拆解为以下6种参数组合,并回填至原始参数中,驱动同一个测试用例执行,拆分后的数据配置如下:

第1组 参数组合:
$.jsonObjectDemo.sentenceDesc.desc:数据驱动
$.jsonObjectDemo.grade[0]:99
第1组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["99",33],"sentenceDesc":{"sentence":"你好","desc":"数据驱动"},"Id":"244"}}

第2组 参数组合:
$.jsonObjectDemo.sentenceDesc.desc:数据驱动
$.jsonObjectDemo.grade[0]:88
第2组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["88",33],"sentenceDesc":{"sentence":"你好","desc":"数据驱动"},"Id":"244"}}

第3组 参数组合:
$.jsonObjectDemo.sentenceDesc.desc:数据驱动
$.jsonObjectDemo.grade[0]:77
第3组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["77",33],"sentenceDesc":{"sentence":"你好","desc":"数据驱动"},"Id":"244"}}

第4组 参数组合:
$.jsonObjectDemo.sentenceDesc.desc:参数化
$.jsonObjectDemo.grade[0]:99
第4组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["99",33],"sentenceDesc":{"sentence":"你好","desc":"参数化"},"Id":"244"}}

第5组 参数组合:
$.jsonObjectDemo.sentenceDesc.desc:参数化
$.jsonObjectDemo.grade[0]:88
第5组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["88",33],"sentenceDesc":{"sentence":"你好","desc":"参数化"},"Id":"244"}}

第6组 参数组合:
$.jsonObjectDemo.sentenceDesc.desc:参数化
$.jsonObjectDemo.grade[0]:77
第6组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["77",33],"sentenceDesc":{"sentence":"你好","desc":"参数化"},"Id":"244"}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

Wady与原生TestNG覆盖同样的测试点,数据驱动配置数量差异如下:

image-20201223142358421

# 3.1.2.2 顺序排列

排列组合的方式适合与参数之间不存在关联关系的场景,若当上述例子中desc,grade存在关联关系时,我们可以使用顺序组合的方式,如下:

---
testcase: 验证 XX 功能是否符合预期

# 参数化配置
parameterization:
  jsonObjectDemo.sentenceDesc.desc: 数据驱动,参数化
  jsonObjectDemo.grade[0]: 99,88,77

# 参数组合方式,非必填,支持顺序和排列组合,默认排列组合
parameterCombination: sequential

# 参数配置
parameter:
  # 参数配置
  jsonObjectDemo: {"Id":"244","code":"Connect","sentenceDesc":{"desc":"","sentence":"你好"},"grade":[59, 33]}

# 期望配置
expectResult:
  expect: {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

当我们选择 顺序组合(parameterCombination: sequential) 时,每组参数取相同索引位置的元素进行组合。如上数据配置可生成以下3种参数组合,并回填至原始参数中,如下:

第1组 参数组合:
jsonObjectDemo.sentenceDesc.desc:数据驱动
jsonObjectDemo.grade[0]:99
第1组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["99",33],"sentenceDesc":{"sentence":"你好","desc":"数据驱动"},"Id":"244"}}

第2组 参数组合:
jsonObjectDemo.sentenceDesc.desc:参数化
jsonObjectDemo.grade[0]:88
第2组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["88",33],"sentenceDesc":{"sentence":"你好","desc":"参数化"},"Id":"244"}}

第3组 参数组合:
jsonObjectDemo.grade[0]:77
第3组 参数组合回填原始参数:
{"jsonObjectDemo":{"code":"Connect","grade":["77",33],"sentenceDesc":{"sentence":"你好","desc":""},"Id":"244"}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

另外在这顺序组合的场景下,也支持了对期望结果的参数化设置,使得实现期望结果与入参保持一定的关联性,如下

# 参数化配置
parameterization:
  jsonObjectDemo.sentenceDesc.desc: 入参参数化1,入参参数化2
  jsonObjectDemo.grade[0]: 99,88,77
  expect.sentenceDesc.desc: 期望结果参数化1,期望结果参数化2
# 参数组合方式,非必填,支持顺序和排列组合,默认排列组合
parameterCombination: sequential
# 参数配置
parameter:
  # 参数配置
  jsonObjectDemo: {"Id":"244","code":"Connect","sentenceDesc":{"desc":"","sentence":"你好"},"grade":[59, 33]}
# 期望配置
expectResult:
  expect: {"Id":"244","code":"Connect","sentenceDesc":{"desc":"描述","sentence":"你好"},"grade":[77, 33]}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

根据上述配置,我们不仅可以将每组参数取相同索引位置的元素进行组合,也一同实现了相同所以位置的期望结果的组合。需要注意的是,期望结果的参数化,建议只应用于当parameterCombination为sequential时。

# 3.1.3 更加灵活的结果比对方式

为满足不同场景的结果验证需求,Wady 支持三种维度的验证如下:

  • 校验精度:精确(precision)、模糊(fuzzy)
  • 校验方式:排除(exclude)、选择(choose)、全部(默认)
  • 校验范围:指定key、全覆盖(默认)

当我们有这些需求时,只需要在配置文件中添加check配置项即可,如下:

# 校验方式,支持精确比较和非空校验,非必填
check:
  # 校验精度,支持精确校验、模糊经验,默认精准,非必填
  precision: precision
  # 校验范围,默认全校验,非必填
  key: sentenceDesc.desc
  # 校验方式:选择校验、过滤校验、默认全校验,非必填
  way: choose
1
2
3
4
5
6
7
8

# 3.2 关于单元测试的用例代码

单元测试用例,一图抵千言,如下:

IMG_3553

# 3.2.1 统一的数据驱动方法

实现AbstractAiTestFramework类提供的统一数据驱动方法(TestDataProvider),该方法根据实现根据类名、方法名在指定yaml文件目录下(存放数据驱动数据)加载对应的yaml文件中测试数据,同时当无其对应的yaml文件时,会自动创建该文件。TestDataProvider根据yaml中多文档快的数据隔离的特性,使用不同块依次驱动测试,实现数据驱动,如下:

package frameworkTest;

import framework.factory.AbstractAiTestFramework;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.Map;

public class CompareJsonTest extends AbstractAiTestFramework {
    @Test(dataProvider = "TestDataProvider")
    public void compareJsonObjectTest(Map<String, Object> parameter){
        Assert.assertNotNull();
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3.2.2 统一的配置获取方式

Wady通过com.alibaba.fastjson.JSONPath灵活获取配置中具体的内容,如:

// 获取参数
JSONObject methodParameter = (JSONObject) JSONPath.eval(paramsObj,"$.parameter.jsonObjecta");

// 方法调用, 此处省略. 假设 methodParameter 同样作为方法返回结果.
... 
    
// 获取期望结果
JSONObject expectResult = (JSONObject) JSONPath.eval(paramsObj,"$.expectResult.expect");
1
2
3
4
5
6
7
8

# 3.2.3 统一的结果比对方式

我们通过CompareJsonUtils.compareJson方法中传入实际结果、期望结果以及yaml配置信息,即可根据yaml中配置的方式实现灵活的比较。降低对测试脚本的人工干预,为后续自动生成测试脚本提供可能。

// 统一的结果比对接口, 根据配置实现即可灵活选择、过滤比对方式 及精确、模糊校验角度.
CompareBaseResult compareBaseResultDTO = CompareJsonUtils.compareJson(methodParameter, expectResult, paramsObj);
1
2

因为Wady实现了精确比较,并且记录了不一致的个数,因此我们在断言时,仅需断言不一致个数是否为0即可,如下:

// 结果断言
Assert.assertEquals(compareBaseResultDTO.getRetCode(), 0,String.valueOf(compareBaseResultDTO.getRetValue()));
1
2

当实际结果与期望结果存在不一致的情况时,支持输出每个细节的比对结果,并且将不一致的详细信息记录下来,同时输出不一致的具体信息,如下:

image-20201223144708254

Wady与原生TestNG覆盖同样的测试点,单元测试代码量差异如下:

image-20201223143730950

# 4 致谢

感谢以下同学,在整体框架设计、优化、应用过程中的努力付出,以及提出的宝贵意见,以下排名不分先后:

姜越、郭群、张天宇、马杰

#测试框架#SDK测试#单元测试#TestNG
上次更新: 7/26/2026, 3:17:15 PM
测试用例
Wady在SDK测试中的应用

← 测试用例 Wady在SDK测试中的应用→

最近更新
01
测试覆盖度量全景图
11-30
02
Prism测试覆盖度量
11-30
03
AITDBClient使用文档
11-15
更多文章>
Theme by Vdoing | Copyright © 2021-2026 toddlerya | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式