package direct; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.JSONCompareResult; import org.skyscreamer.jsonassert.comparator.DefaultComparator; import org.skyscreamer.jsonassert.JSONCompare; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.HashMap; import java.util.Map; import static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.*; /** * Created by jenny zhang on 2017/9/19. */ public class LooseyJSONComparator extends DefaultComparator { private int scale; //精度,scale=5,表示精确到小数点后5位 private Double accuracy; //精度,accuracy=0.00001,表示精确到小数点后5位 String extraInfo; def log; public LooseyJSONComparator(JSONCompareMode mode, int scale,String extraInfo,def log) { super(mode); this.scale = scale; this.accuracy = 1.0/Math.pow(10,scale); this.extraInfo = extraInfo; this.log = log; } public static void assertEquals( String expected, String actual, int scale, String extraInfo, def log) throws JSONException { JSONCompareResult result = JSONCompare.compareJSON(expected, actual, new LooseyJSONComparator(JSONCompareMode.NON_EXTENSIBLE,scale,extraInfo,log)); if (result.failed()) { def failMessage = result.getMessage(); throw new AssertionError(extraInfo + failMessage); } else{ log.info "pass"; } } @Override protected void compareJSONArrayOfJsonObjects(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException { String uniqueKey = findUniqueKey(expected); if (uniqueKey == null || !isUsableAsUniqueKey(uniqueKey, actual)) { // An expensive last resort recursivelyCompareJSONArray(key, expected, actual, result); return; } Map<Object, JSONObject> expectedValueMap = arrayOfJsonObjectToMap(expected, uniqueKey, log); Map<Object, JSONObject> actualValueMap = arrayOfJsonObjectToMap(actual, uniqueKey, log); for (Object id : expectedValueMap.keySet()) { if (!actualValueMap.containsKey(id)) { result.missing(formatUniqueKey(key, uniqueKey, expectedValueMap.get(id).get(uniqueKey)), expectedValueMap.get(id)); continue; } JSONObject expectedValue = expectedValueMap.get(id); JSONObject actualValue = actualValueMap.get(id); compareValues(formatUniqueKey(key, uniqueKey, id), expectedValue, actualValue, result); } for (Object id : actualValueMap.keySet()) { if (!expectedValueMap.containsKey(id)) { result.unexpected(formatUniqueKey(key, uniqueKey, actualValueMap.get(id).get(uniqueKey)), actualValueMap.get(id)); } } } private String getCompareValue(String value) { try{ return new BigDecimal(value).setScale(scale, RoundingMode.HALF_EVEN).toString(); } catch (NumberFormatException e) { return value; //value may = NaN, in this case, return value directly. } } private boolean isNumeric(Object value) { try { Double.parseDouble(value.toString()); return true; } catch (NumberFormatException e) { return false; } } public Map<Object, JSONObject> arrayOfJsonObjectToMap(JSONArray array, String uniqueKey,def log) throws JSONException { Map<Object, JSONObject> valueMap = new HashMap<Object, JSONObject>(); for (int i = 0; i < array.length(); ++i) { JSONObject jsonObject = (JSONObject) array.get(i); Object id = jsonObject.get(uniqueKey); id = isNumeric(id) ? getCompareValue(id.toString()) : id; valueMap.put(id, jsonObject); } return valueMap; } @Override public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException { if (areLeaf(expectedValue, actualValue)) { if (isNumeric(expectedValue) && isNumeric(actualValue)) { //For special numeric, such as NaN, convert to string to compare boolean equalsAsSpecialNumeric = (expectedValue.toString().equals(actualValue.toString())) //For normal numeric, such as 153.6960001, 1.56E5, subtract and then get abosolute value boolean equalsAsGeneralNumeric = Math.abs(Double.parseDouble(expectedValue.toString())-Double.parseDouble(actualValue.toString()))<accuracy; if (equalsAsSpecialNumeric||equalsAsGeneralNumeric) { result.passed(); } else { result.fail(prefix, expectedValue, actualValue); } return; } } super.compareValues(prefix, expectedValue, actualValue, result); } private boolean areLeaf(Object expectedValue, Object actualValue) { boolean isLeafExpectedValue = !(expectedValue instanceof JSONArray)&&!(expectedValue instanceof JSONObject); boolean isLeafActualValue = !(actualValue instanceof JSONArray)&&!(actualValue instanceof JSONObject); return isLeafExpectedValue&&isLeafActualValue; } }
SoapUI 里面进行调用:
package direct import org.json.* //Get test step name def currentStepIndex = context.currentStepIndex def previousStep = testRunner.testCase.getTestStepAt(currentStepIndex-1) def prePreStep = testRunner.testCase.getTestStepAt(currentStepIndex-2) def previousStepName = previousStep.name def prePreStepName = prePreStep.name //Get extra information, get case number in loop String caseNum = context.expand('${DataSource#caseNumber}') String extraInfo = caseNum+ ", " //Get more extra information, get request ID try{ def requestIdTest =previousStep.testRequest.messageExchange.requestHeaders.get("X-API-RequestId") def requestIdBmk =prePreStep.testRequest.messageExchange.requestHeaders.get("X-API-RequestId") extraInfo += "requestIdBmk = "+requestIdBmk+", requestIdTest = "+requestIdTest+", " } catch(NullPointerException e){ assert false,extraInfo+"HTTP Request is null" } //Get json response and compare them try{ String expectedJsonResponse = new JSONObject(context.expand( '${'+prePreStepName+'#Response}' )) String actualJsonResponse = new JSONObject(context.expand( '${'+previousStepName+'#Response}' )) LooseyJSONComparator.assertEquals(expectedJsonResponse, actualJsonResponse,5,extraInfo,log) } catch(JSONException e){ assert false,extraInfo+"HTTP Response is not JSON" }