我们在接口自动化测试过程中,或多或少会遇到当比较接口返回结果时,如果将结果作为一个整体的对象进行比较,假如断言失败时,那我们将会看到一大坨期望和实际结果数据,很难一眼就看出具体是因为哪一部分不一致而导致的比对失败,这也增加了我们的接口自动化维护成本。
本文主要围绕这一痛点,解决 JsonObject、JSONArray、String、Integer 对象的精确比较问题,并提供统一的Java实现方法 Compare.CompareFactory,希望对大家有所帮助。
先睹为快,让我们直接来看下效果如何,此外,文末附 Python 具体实现设计。
JSONObject 对象的一致性比对
如下,先准备两个测试数据,我们可以看到测试数据的结构还是比较复杂的,其中左侧和右侧不一致的地方共有7处,如黄色框处的位置。
首先看一下,如果直接断言,我们看到的结果是什么样的。
正如开篇所提,想要找出其中的不同,实在... 简直... 。
然后,使用 Compare.CompareFactory 方法,执行过程中的打印结果如下,我们可以看到每个节点的比较结果都非常清晰的打印出来了,并且key 的描述也是含JSON层级关系的,如 "regional.city[0]" 。
再然后,看下 Compare.CompareFactory 方法的返回结果,我们将返回对象使用JSONObject.toJSONString 转换成字符串后,如下,resultDetail 属性记录了每个错误节点的期望和实际数据值,wrongNumber 属性统计了错误数量,清晰明了,简单直白。
最后,看下代码,因为我将测试数据放到 yaml 文件中,因为其中部分代码是在加载测试数据。
JSONArray 对象的一致性比对
首先,准备如下两个测试数据,其中左侧和右侧不一致的地方共有 5 处,如黄色框处的位置。
然后,使用 Compare.CompareFactory 方法,执行过程中的打印结果如下:
再然后,我们看下 Compare.CompareFactory 方法的返回结果,如下:
最后,我们看下示例代码,因为我将测试数据放到Yaml文件中,所以其中部分代码是在加载测试数据。
String、Integer、Long 对象的一致性比对
String、Integer、Long 对象的比对,同样也是使用 Compare.CompareFactory 方法完成,示例代码如下:
因为String、Integer、Long不存在KV映射,因此比较结果中key显示为null。如下:
还提供了哪些特性?
肯定有小伙伴会问,在做接口返回结果的校验时,并不是全部的数据都需要一一进行比对,比如时间属性,因为每次可能返回的时间不同,但本身不影响结果的准确性,所以不需要进行比较。 那Compare.CompareFactory 方法是否不支持过滤某些属性不比较呢?——那当然是支持的咯,细心的小伙伴 Compare.CompareFactory 的有三个参数,那第三个是干嘛的呢?其实第三个参数就是描述需要过滤的属性的,如果需要过滤多个属性使用英文逗号隔开即可。
比如,我们将上图例子中的实际和期望结果不一致的 code、designation、hobby_id、desc 4个属性过滤掉不比较,代码如下。
运行代码,我们可以看到比对结果中,只存在三处不一致的地方,即实现了过滤比较。
核心源码
因涉及多个java文件,这里只能贴出核心代码,如下:
import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONArray; import org.apache.commons.lang3.StringUtils; public class Compare { private static BaseResult baseResult; /** * @Description: 更新结果,错误数量计数 * @Param: [parameter] * @Author: * @DateTimeFormat: 2020/3/8 */ private static void updateBaseResult(ResultDetail resultDetail) { // 错误数量统计 baseResult.setWrongNumber(baseResult.getWrongNumber() + 1); // 填充 RetValue 错误内容 if (baseResult.getResultDetail() == null) { ArrayList resultDetailArrayList = new ArrayList(); resultDetailArrayList.add(resultDetail); baseResult.setResultDetail(resultDetailArrayList); } else { baseResult.getResultDetail().add(resultDetail); } } /** * @Description: 字符串比较 * @Param: [parameter] * @return: void * @Author: YJiang * @DateTimeFormat: 2020/3/8 */ private static void compareJson(String actualString, String expectString, String key, String prefix , String ignore) { boolean status = false; if (StringUtils.isNotBlank(ignore)) { List listIgnore = Arrays.asList(ignore.split(",")); status = listIgnore.contains(key); } if (!status) { if (!actualString.equals(expectString)) { ResultDetail resultDetail = new ResultDetail(prefix, actualString, expectString, "Value Not Equal"); updateBaseResult(resultDetail); System.out.println( "033[1;31m" + "【fail】Key:" + prefix + " actualJson:" + actualString + " expectJson:" + expectString + "033[m"); } else { System.out.println( "033[1;94m" + "【success】Key:" + prefix + " actualJson:" + actualString + " expectJson:" + expectString + "033[m"); } } } /** * @Description: JSONObject 比较 * @Param: [parameter] * @return: void * @Author: YJiang * @DateTimeFormat: 2020/3/8 */ private static void compareJson(JSONObject actualJson, JSONObject expectJson, String key,String prefix, String ignore) { if (StringUtils.isBlank(prefix)){ prefix = ""; }else { prefix = prefix + "."; } for (String s : expectJson.keySet()) { key = s; compareJson(actualJson.get(key), expectJson.get(key), key, prefix + key, ignore); } } /** * @Description: JSONArray 比较 * @Param: [parameter] * @return: void * @Author: YJiang * @DateTimeFormat: 2020/3/8 */ private static void compareJson(JSONArray actualJsonArray, JSONArray expectJsonArray, String key,String prefix, String ignore) { if (actualJsonArray != null && expectJsonArray != null) { if (actualJsonArray.size() == expectJsonArray.size()) { Iterator iteratorActualJsonArray = actualJsonArray.iterator(); if (StringUtils.isBlank(prefix)){ prefix = ""; } int num = 0; for (Object o : expectJsonArray) { compareJson(iteratorActualJsonArray.next(), o, key,prefix+"["+num+"]", ignore); num ++; } } else { ResultDetail resultDetail = new ResultDetail(prefix, actualJsonArray, expectJsonArray, "Length Not Equal"); updateBaseResult(resultDetail); } } else { if (actualJsonArray == null && expectJsonArray == null) { ResultDetail resultDetail = new ResultDetail(prefix, "在 actualJsonArray 中不存在", "在 expectJsonArray 中不存在", "Both Not Exist"); updateBaseResult(resultDetail); } else if (actualJsonArray == null) { ResultDetail resultDetail = new ResultDetail(prefix, "在 actualJsonArray 中不存在", expectJsonArray, "Other Exist"); updateBaseResult(resultDetail); } else { ResultDetail resultDetail = new ResultDetail(prefix, actualJsonArray, "在 expectJsonArray 中不存在", "Other Exist"); updateBaseResult(resultDetail); } } } /** * @Description: Object 比较 * @Param: [parameter] * @return: void * @Author: YJiang * @DateTimeFormat: 2020/3/8 */ private static void compareJson(Object actualJson, Object expectJson, String key,String prefix, String ignore) { if (actualJson != null && expectJson != null) { if (actualJson instanceof JSONObject) { compareJson((JSONObject)actualJson, (JSONObject)expectJson, key,prefix, ignore); } else if (actualJson instanceof JSONArray) { compareJson((JSONArray)actualJson, (JSONArray)expectJson, key,prefix, ignore); } else if (actualJson instanceof String) { try { String actualJsonToStr = actualJson.toString(); String expectJsonToStr = expectJson.toString(); compareJson(actualJsonToStr, expectJsonToStr, key,prefix, ignore); } catch (Exception e) { ResultDetail resultDetail = new ResultDetail(prefix, actualJson, expectJson, "String 转换发生异常 Key"); updateBaseResult(resultDetail); e.printStackTrace(); } } else { compareJson(actualJson.toString(), expectJson.toString(), key,prefix, ignore); } } else { if (actualJson == null && expectJson == null) { ResultDetail resultDetail = new ResultDetail(prefix, "在actualJson中不存在", "在expectJson中不存在", "Both Not Exist"); updateBaseResult(resultDetail); } else if (actualJson == null) { ResultDetail resultDetail = new ResultDetail(prefix, "在actualJson中不存在", expectJson, "Other Exist"); updateBaseResult(resultDetail); } else{ ResultDetail resultDetail = new ResultDetail(prefix, actualJson, "在expectJson中不存在", "Not Exist"); updateBaseResult(resultDetail); } } } /** 非线性安全 * @Description: CompareFactory * @Param: [parameter] * @return: void * @Author: YJiang * @DateTimeFormat: 2020/3/8 */ public static BaseResult CompareFactory(T actual, T expect, String ignore) { Compare.baseResult = new BaseResult(); Compare.compareJson(actual, expect, null,null, ignore); return Compare.baseResult; } }
对了,之前写过JSON 一致性比对的Python实现,有兴趣的同学可以看一下哦 。
Json数据灵活的一致性校验的整体设计与具体实现