請教下有沒有在 slb 或者 nginx 層面實施 mock 的開源框架,最好是 java 的

Vanessa發表於2024-11-13

公司用的 nacos 做服務管理,還是 1.4 的老版本,同時還有一些老專案沒接 nacos 直接走 http 請求,測試目前有一套自研的 mock 工具,但是對程式碼有一些侵入,而且有些專案存在依賴不相容問題。
所以現在想一步到位在 ng 層面做 mock 解決所有問題,請教下各位大佬有沒有開源的 mock 框架,或者有沒有其他好的解決方案。

順便就分享下現用的 mock 工具,原理就是在被 mock 類上加註解,然後透過反射替換返回值或者請求到 mock 服務上。
主要實現 2 個功能:

  1. 根據配置中心的配置直接返回被 mock 方法返回值
  2. 根據環境將請求轉發到對應環境的 mock 服務,然後在 mock 服務裡面處理邏輯。

領導覺得太 low 了讓在 ng 層實現,所以希望有能在 ng 層實現上面倆功能的工具,求大佬們推薦。

@Aspect
public class ConfMockAspect {
    @Autowired
    SpelExcetor spelExcetor;
    private final static Logger logger = LoggerFactory.getLogger(ConfMockAspect.class);
    private static String mock_system_test_url = "http://xxxx";
    private static String mock_system_dev_url = "http://xxxx";

    @Pointcut("@within(com.mock.ConfMock)")
    private void aspect() {}

    @Around("aspect()")
    public Object around(JoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        Class<?> targetClass = target.getClass();
        Class<?>[] interfaces = targetClass.getInterfaces();
        String sName = targetClass.getSimpleName();
        MethodSignature sig = (MethodSignature)joinPoint.getSignature();
        Method method = sig.getMethod();
        String methodName = method.getName();
        Type type = method.getGenericReturnType();
        String key = "ConfMock." + sName + "." + methodName;
        // 走mock系統
        String sysKey1 = "system.ConfMock." + sName;
        String sysKey2 = "system.ConfMock." + sName + "." + methodName;
        String value = null;
        if (LocalCacheRepository.getInstance().get(sysKey1) != null
            || LocalCacheRepository.getInstance().get(sysKey2) != null) {
            String url = null;
            if (PropertyConstant.CONF_PROD_DEV.equals(AppInstance.getEnv())) {
                url = mock_system_dev_url + String.format("/api?className=%s&method=%s&paramClass=%s",
                    interfaces[0].getName(), methodName, method.getParameterTypes()[0].getName());
            } else if (PropertyConstant.CONF_PROD_TEST.equals(AppInstance.getEnv())){
                url = mock_system_test_url + String.format("/api?className=%s&method=%s&paramClass=%s",
                    interfaces[0].getName(), methodName, method.getParameterTypes()[0].getName());
            }  else {
                url = mock_system_uat_url + String.format("/api?className=%s&method=%s&paramClass=%s",
                    interfaces[0].getName(), methodName, method.getParameterTypes()[0].getName());
            }
            String param = JSON.toJSONString(joinPoint.getArgs()[0], new LengthFilter());
            value = HttpUtil.doPost(param, new HashMap<>(), url);
            if (value != null) {
                return JSON.parseObject(value, type);
            }
        } else {
            value = getValue(joinPoint, key);
            if (value != null) {
                Object result = parseReturn(value, type, joinPoint.getArgs());
                return result;
            }
        }
        return ((ProceedingJoinPoint)joinPoint).proceed();
    }

    private String getValue(JoinPoint joinPoint, String key) {
        try {
            Properties properties = LocalCacheRepository.getInstance().getProperties();
            Set<Object> set = properties.keySet();
            for (Object srt : set) {
                String keyStr = srt.toString();
                if (keyStr.startsWith(key)) {
                    String s = org.apache.commons.lang3.StringUtils.removeStart(keyStr, key);
                    if (!StringUtils.isEmpty(s)) {
                        ExpressionParser parser = new SpelExpressionParser();
                        StandardEvaluationContext context = new StandardEvaluationContext();
                        context.setRootObject(joinPoint.getArgs());
                        Boolean result =
                            (Boolean)parser.parseExpression(org.apache.commons.lang3.StringUtils.removeStart(s, "."))
                                .getValue(context);
                        if (result) {
                            return properties.getProperty(keyStr);
                        }
                    }
                }
            }
            if (properties.containsKey(key)) {
                return properties.getProperty(key);
            }
        } catch (Exception e) {
            logger.warn("解析key失敗", e);
        }
        return null;
    }

    private Object parseReturn(String value, Type type, Object[] args) {
        if (String.class.getTypeName().equals(type.getTypeName())) {
            return value;
        }
        Object object = JSON.parse(value);
        if (object instanceof JSONArray) {
            JSONArray array = (JSONArray)object;
            parseValue(array, args);
            return array.toJavaObject(type);
        } else if (object instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject)object;
            parseValue(jsonObject, args);
            return jsonObject.toJavaObject(type);
        }
        return object;
    }

    private void parseValue(JSONArray array, Object rootObject) {
        if (array != null && !array.isEmpty()) {
            for (int i = 0; i < array.size(); i++) {
                Object value = array.get(i);
                if (value != null) {
                    if (value instanceof String) {
                        Object newValue = doneInSpringContext((String)value, rootObject);
                        if (newValue != null) {
                            array.set(i, newValue);
                        }
                    } else if (value instanceof JSONObject) {
                        parseValue((JSONObject)value, rootObject);
                    } else if (value instanceof JSONArray) {
                        parseValue((JSONArray)value, rootObject);
                    }
                }
            }
        }
    }

    private void parseValue(JSONObject object, Object rootObject) {
        Set<String> set = object.keySet();
        if (!set.isEmpty()) {
            for (String key : set) {
                Object value = object.get(key);
                if (value != null) {
                    if (value instanceof String) {
                        if (isSpel((String)value)) {
                            Object newValue = doneInSpringContext(spelValue((String)value), rootObject);
                            if (newValue != null) {
                                object.put(key, newValue);
                            }
                        }
                    } else if (value instanceof JSONObject) {
                        parseValue((JSONObject)value, rootObject);
                    } else if (value instanceof JSONArray) {
                        parseValue((JSONArray)value, rootObject);
                    }
                }
            }
        }
    }

    private static boolean isSpel(String value) {
        // ${}
        if (value != null && value.startsWith("${") && value.endsWith("}")) {
            return true;
        }
        return false;
    }

    public static String spelValue(String value) {
        if (isSpel(value)) {
            value = org.apache.commons.lang3.StringUtils.removeFirst(value, "\\$\\{");
            value = org.apache.commons.lang3.StringUtils.removeEnd(value, "}");
            return value;
        }
        return value;
    }

    private Object doneInSpringContext(String value, Object rootObject) {
        try {
            return spelExcetor.doneInSpringContext(null, rootObject, value);
        } catch (Exception e) {
        }
        return null;
    }
    public static class LengthFilter implements ValueFilter{

        @Override
        public Object process(Object object, String name, Object value) {
            if (value != null && value instanceof String) {
                String var = (String)value;
                if (var.length() > 500) {
                    return var.substring(0, 500);
                }
            }
            return value;
        }

    }

相關文章