Custom Verify Class for JsonPath Validation with Groovy Operator Overloading
This article introduces a Groovy‑based Verify utility that overloads arithmetic and comparison operators to simplify JsonPath response validation, eliminating manual type conversions and enabling expressive syntax for front‑end test cases.
When using the JsonPath utility class for API response validation, the native JsonPath API returns values as objects, requiring extra conversion code.
This limitation is inconvenient for front‑end users who write test cases with textual markup; a more natural syntax using operators like >, +, /, = would be helpful.
Combining this need with Groovy operator overloading techniques, a custom Verify class was created to provide overloaded operators for JsonPath verification.
Code
package com.fun.utils
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONException
import com.alibaba.fastjson.JSONObject
import com.fun.frame.SourceCode
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
* 操作符重写类,用于匹配JSonpath验证语法,基本重载的方法以及各种比较方法,每个方法重载三次,参数为double,String,verify
* 数字统一采用double类型,无法操作的String对象的方法返回empty
*/
class Verify extends SourceCode implements Comparable {
public static Logger logger = LoggerFactory.getLogger(Verify.class)
/**
* 验证文本
*/
String extra
/**
* 验证数字格式
*/
double num
/**
* 构造方法,暂时写着,尽量使用jsonutil创造verify对象
* @param json
* @param path
*/
private Verify(JSONObject json, String path) {
this(JsonUtil.getInstance(json).getString(path))
if (isNumber()) num = changeStringToDouble(extra)
}
private Verify(String value) {
extra = value
logger.info("构建verify对象:{}",extra)
if (isNumber()) num = changeStringToDouble(extra)
}
/**
* 获取实例方法
* @param json
* @param path
* @return
*/
static Verify getInstance(JSONObject json, String path) {
new Verify(json, path)
}
static Verify getInstance(String str) {
new Verify(str)
}
/**
* 加法重载
* @param i
* @return
*/
def plus(double i) { isNumber() ? num + i : extra + i.toString() }
/**
* 加法重载,string类型
* @param s
* @return
*/
def plus(String s) { isNumber() && isNumber(s) ? num + changeStringToDouble(s) : extra + s }
/**
* 加法重载,verify类型
* @param s
* @return
*/
def plus(Verify v) { isNumber() && v.isNumber() ? this + (v.num) : extra + v.extra }
/**
* 减法重载
* @param i
* @return
*/
def minus(double i) { isNumber() ? num - i : extra - i.toString() }
/**
* 加法重载,string类型
* @param s
* @return
*/
def minus(String s) { extra - s }
def minus(Verify v) { if (isNumber() && v.isNumber()) this - v.num
extra - v.extra }
/**
* extra * i 这里会去强转double为int,调用intvalue()方法
* @param i
* @return
*/
def multiply(double i) { if (isNumber()) num * i
extra * i }
def multiply(String s) { isNumber() ? isNumber(s) ? num * changeStringToDouble(s) : s * num : isNumber(s) ? extra * changeStringToDouble(s) : EMPTY }
def multiply(Verify v) { this * v.extra }
/**
* 除法重载
* @param i
* @return
*/
def div(int i) { if (isNumber()) num / i }
def div(String s) { if (isNumber() && isNumber(s)) num / changeStringToDouble(s) }
def div(Verify v) { if (isNumber() && v.isNumber()) num / v.num }
def mod(int i) { if (isNumber()) (int) (num % i * 10000) * 1.0 / 10000 }
/**
* 直接取值,用于数组类型
* @param i
* @return
*/
def getAt(int i) { try { JSONArray.parseArray(extra)[i] } catch (JSONException e) { i >= extra.length() ? EMPTY : extra[i] } }
/**
* 直接取值,用户json类型
* @param i
* @return
*/
def getAt(String s) { try { JSON.parseObject(extra)[s] } catch (JSONException e) { extra.indexOf(s) } }
/**
* if (a implements Comparable) { a.compareTo(b) == 0 } else { a.equals(b) }
* @param a
* @return
*/
boolean equals(Verify verify) { extra == verify.extra }
boolean equals(Number n) { num == n.doubleValue() }
boolean equals(String s) { extra == s }
@Override
boolean equals(Object o) { extra == o.toString() }
/**
* a <=> b a.compareTo(b)
* a>b a.compareTo(b) > 0
* a>=b a.compareTo(b) >= 0
* a<b a.compareTo(b) < 0
* a<=b a.compareTo(b) <= 0
* @param o
* @return
*/
@Override
int compareTo(Object o) { if (isNumber() && (o instanceof Number || isNumber(o.toString()))) { return num.compareTo(o.toString() as Double) } else { extra.length().compareTo(o.toString().length()) } }
/**
* 类型转换,用于as关键字
* @param tClass
* @return
*/
def
T asType(Class
tClass) { logger.info("强转类型:{}", tClass.toString())
if (tClass == Integer) num.intValue()
else if (tClass == Double) num
else if (tClass == Long) num.longValue()
else if (tClass == String) extra
else if (tClass == Verify) new Verify(extra)
else if (tClass == Boolean) changeStringToBoolean(extra) }
/**
* 用户正则匹配
* @param regex
* @return
*/
def regex(String regex) { extra ==~ regex }
/**
* 是否是数字
* @return
*/
def isNumber() { isNumber(extra) }
/**
* 是否为boolean类型
* @return
*/
def isBoolean() { extra ==~ ("false|true") }
@Override
String toString() { extra }
}Demo Code
package com.fun.ztest.groovy
import com.fun.frame.httpclient.FanLibrary
import com.fun.utils.Verify
class Ft extends FanLibrary {
public static void main(String[] args) {
def instance1 = Verify.getInstance("fdsafds")
def instance2 = Verify.getInstance("fdsa")
def instance3 = Verify.getInstance("0.2365")
def instance4 = Verify.getInstance("5.0")
def instance5 = Verify.getInstance("5")
println instance1 + instance2
println instance1 - instance2
println instance1 * instance2 as boolean
println instance3 > instance4
println instance3 * instance4
println instance1 * instance4
println instance5 == instance4
println instance5 / instance4
println instance4 as Integer
testOver()
}
}Console Output
INFO-> 当前用户:fv,IP:10.60.192.21,工作目录:/Users/fv/Documents/workspace/fun/,系统编码格式:UTF-8,系统Mac OS X版本:10.15.6
INFO-> 构建verify对象:fdsafds
INFO-> 构建verify对象:fdsa
INFO-> 构建verify对象:0.2365
INFO-> 构建verify对象:5.0
INFO-> 构建verify对象:5
fdsafdsfdsa
fds
false
false
1.1824999999999999
fdsafdsfdsafdsfdsafdsfdsafdsfdsafds
true
1.0
INFO-> 强转类型:class java.lang.Integer
5
Process finished with exit code 0FunTester
10k followers, 1k articles | completely useless
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.