1. 程式人生 > >[SoapUI] 通過JSONAssert比較兩個環境的JSON Response,定製化錯誤資訊到Excel

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