前端API自测小工具的实现

背景

开发JS接口方法过程中,要调试自己的方法,经常写一些测试的数据传到自己的方法上调试,每次修改自己的测试的数据都比较麻烦,而且测试的代码还有忘记删掉的风险。对于QA测试来说测试也比较麻烦,不能灵活的改变参数来测试我们写的方法。

因此这里想办法通过注解配置的形式,自动生成一个测试这类方法的工具。

适用场景

当我们给具体的业务场景或者某个组件调用者提供API的时候,通常情况下都满足这两个条件:
1、各个方法运行的环境一致;
2、各个方法传入的参数类型一致(比如JSONObject、Map等)。

比如,我自己是要给M端提供一些本地方法的api。这时候,我们都可以使用这种方法来做一个测试的小工具。这次的例子将通过JSONObejct作为参数来实现。当然也可以换做其他的参数。

模拟环境

我们先在例子中模拟出开发中一些场景,ContainerActivity作为业务方的运行环境,LocalFunctionAPI为我们开发的api方法。可以看到,业务方可以通过api来传入不同的参数,来满足业务需求。但是我们怎么来测试我们的api呢?

    LocalFunctionAPI mAPI;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_container);
        mAPI = new LocalFunctionAPI(this);

        findViewById(R.id.alert).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                realDo();
            }
        });
    }

    //业务方调用我们api
    private void realDo() {
        JSONObject object = new JSONObject();
        try {
            object.put("msg", "Hello World");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        mAPI.alert(object);
    }

定义注解

要测试我们的api,我们要知道单个接口所有的参数,最好还要有一些预先配置的测试参数。为了更好的知道API的功能,还提供了一个name来显示出来,一目了然。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface APITest{
    //api的名称
    public String name() default "";
    //api的参数,竖线分割
    public String param() default "";
    //api的测试数据,JsonArray形式,内容是JsonObject
    public String testParam() default "";
}

我们的api中方法,就可以根据注解的规则来写注解。

    /**
     * 弹窗的api,参数{"msg":"hello world"}
     * @param object json Object
     */
    @APITest(name = "alert", param = "msg",
            testParam = "[{\"msg\":\"hello world\"}, {\"msg\":\"hello android\"}]")
    public void alert(JSONObject object) {
        // check runtime
        if (context == null) {
            throw new RuntimeException("无运行环境");
        }
        String msg = "";
        // check param
        if (object.has("msg")) {
            try {
                msg = object.getString("msg");
            } catch (JSONException e) {
                e.printStackTrace();
            }
        } else {
            throw new RuntimeException("传入参数不正确");
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle(msg).create().show();
    }

解析注解

可以通过class的一些属性来获取方法和注解信息。

    private List<APITestVo> getApiTestList() {
        List<APITestVo> list = new ArrayList<>();
        Method[] declaredMethods = LocalFunctionAPI.class.getDeclaredMethods();
        for (Method method : declaredMethods) {
            APITest apiTest = method.getAnnotation(APITest.class);
            if (apiTest != null) {
                APITestVo apiTestVo = new APITestVo();
                apiTestVo.setApiTest(apiTest);
                apiTestVo.setMethod(method.getName());
                list.add(apiTestVo);
            }
        }
        return list;
    }

有了数据我们可以展现出API的列表。


function_list.png

JsonObejct转换问题

由于传过来的字符串不能直接用JSONObejct来解析出来,只能先通过Gson的JsonObejct解析出来,然后转换成JSONObejct的形式来做。调试时发现JSONObejct不能直接用,才发现的这里问题,希望大家少走弯路吧。

            mTestVos = new TestParamVo[objects.length];
            for (int i = 0; i < objects.length; i++) {
                TestParamVo paramVo = new TestParamVo();
                JsonObject object = objects[i];
                Set<String> keys = object.keySet();
                JSONObject jsonObject = new JSONObject();
                for (String key : keys) {
                    try {
                        JsonElement jsonElement = object.get(key);
                        jsonObject.put(key, jsonElement.isJsonObject() ? jsonElement.getAsJsonObject().toString() : jsonElement.getAsString());

                 // 之前的版本是这样的:jsonObject.put(key, object.get(key).getAsString());
                // 解析字符串中套入jsonObject会有问题。
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
                paramVo.setParam(jsonObject);
                mTestVos[i] = paramVo;
            }

测试环境

测试的Activity中构建一个和ContainerAcitivity一样的空间,只是页面不同。这里的页面来展现出解析的测试参数和值。分别用两个list来实现。这里代码不在赘述,就是一些简单的交互。

image.png

当点击执行的时候,获取所有的参数,组装参数,反射执行api的方法:

                //反射执行
                JSONObject jsonObject = new JSONObject();
                for (ParamVo paramVo : mParamVos) {
                    if (!TextUtils.isEmpty(paramVo.getValue())) {
                        try {
                            jsonObject.put(paramVo.getName(), paramVo.getValue());
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                }
                Method[] declaredMethods = mAPI.getClass().getDeclaredMethods();
                boolean hasExecute = false;
                for (Method method : declaredMethods) {
                    if (mMethodName.equals(method.getName())) {
                        try {
                            hasExecute = true;
                            method.invoke(mAPI, jsonObject);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();

                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
                if (!hasExecute) {
                    Toast.makeText(TestAPIActivity.this, "没有这个方法", Toast.LENGTH_SHORT).show();
                }

总结

生活工作中应该多多思考,尤其是当发现我们经常做一些重复繁琐的事情的时候就应该多想想是不是有什么解决方案来解决这些问题呢?

希望这个例子能给大家带来一些帮助,这个例子的源码都在我自己的github上的 FunctionTest 项目中能找到。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容