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;
}
}