[SoapUI] 通过JSONAssert比较两个环境的JSON Response,定制化错误信息到Excel
package ScriptLibrary; 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; ArrayList<String> ignoreKeys; def testRunner def context def extraInfoMap 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 LooseyJSONComparator(JSONCompareMode mode, int scale,def log,def context,def testRunner,def extraInfoMap,ArrayList<String> ignoreKeys) { super(mode); this.scale = scale; this.accuracy = 1.0/Math.pow(10,scale); this.log = log; this.context = context this.testRunner = testRunner this.extraInfoMap = extraInfoMap this.ignoreKeys = ignoreKeys; } public static void assertEquals( String expected, String actual, int scale, def log,def context=null,def testRunner=null,def extraInfoMap=null,ArrayList<String> ignoreKeys=null) throws JSONException { JSONCompareResult result = JSONCompare.compareJSON(expected, actual, new LooseyJSONComparator(JSONCompareMode.NON_EXTENSIBLE,scale,log,testRunner,context,extraInfoMap,ignoreKeys)); if (result.failed()) { def currentStepIndex = context.currentStepIndex def testSuiteName = testRunner.testCase.getTestStepAt(currentStepIndex).getParent().getParent().getName() def failMessage = result.getMessage(); if(failMessage.contains("Expected:")&&failMessage.contains("got:")&&(testRunner!=null)&&(context!=null)&&(extraInfoMap!=null)){ //移除老板不想看的信息 def keysToRemoveForBoss = ["RequestIdBmk", "RequestIdTest"] def extraInfoMapForBoss = extraInfoMap.findAll({!keysToRemoveForBoss.contains(it.key)}) def topRow=["Test Suite","Test Case"] topRow = topRow+extraInfoMapForBoss.keySet() topRow = topRow+["DataPoint","Bmk Env","Test Env","DetailedJsonPath"] //获取当前自动化测试Project所在的路径 String projectPath = context.expand( '${projectDir}' ) WriteExcel writeExcel = new WriteExcel(testRunner,context,log) //处理fail message,解析出来写入excel ArrayList failMessageList = new ArrayList() failMessageList = writeExcel.parseFailMessage(failMessage,extraInfoMapForBoss) //定义Excel的路径和名字 String filePath = projectPath+ "/TestResult/" + testSuiteName + "_Fail.xlsx" File testResultFile = new File(filePath) //如果Excel不存在,就先创建带列名的Excel if(!testResultFile.exists()){ writeExcel.createExcel(testResultFile,topRow) } //追加信息到Excel writeExcel.addExcel(filePath,failMessageList) } //打印QA和Developer想要的错误信息到SoapUI报告 def extraInfoForQAAndDev = extraInfoMap.collect { k,v -> "$k=$v" }.join(',') throw new AssertionError(extraInfoForQAAndDev+failMessage); } else{ log.info "pass"; } } 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)); } } } @Override protected void checkJsonObjectKeysActualInExpected(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) { Set<String> actualKeys = getKeys(actual); for (String key : actualKeys) { if ((!expected.has(key))&&(!ignoreKeys.find{it == key})) { result.unexpected(prefix, key); } } } 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; } }