如何统一接口测试的功能、自动化和性能测试用例

服务端的测试,大多数内容都围绕着接口展开。对于接口测试,无非也是功能、自动化、性能测试为主,偶然想到一个问题,如果能写一个用例,在功能、自动化、性能三者的测试场景中重复使用,肯定能省去非常多的时间。

总体思路跟之前的接口和性能框架一样,通过总的测试项目中把接口功能封装好,留出来测试参数。功能测试就写方法调用然后人肉检查,自动化的话把接口响应拿出来,然后验证各种数据,性能直接使用性能框架直接调用该方法即可。

花了点时间,做了一个Demo,分享给大家。

这是一个简易的接口请求方法,其中main方法里面是功能测试执行代码,用例是文档形式,这里就不写了:


public class Headgear extends NajmBase {


    public Headgear(NajmBase najmBase) {
        this.loginKey = najmBase.loginKey;
        this.args = najmBase.args;
        this.user_id = najmBase.user_id;
    }

    private static NajmBase base = NajmBase.getBase(0);

    public static Headgear drive = new Headgear(base);


    /**
     * 当前正在使用的头套
     */

    public int usingHeadgearId;

    //  public JSONObject headgearInfo = new JSONObject();
    public Map<Integer, Long> headgearInfo = new HashMap<>();

    public static void main(String[] args) {
//      NajmBase.getUserBalance(base.user_id);
//      int type = 1, id = 36, packageId = 60, num = 1, price = 1;
//      NajmBase base1 = new NajmBase(V580User.getUserName(0));
//      Headgear headgear = new Headgear();
//      headgear.switchHeadgear(34);
//      output(headgear.getHeadgearInfo());
//      output(headgear.getUsingHeadgearId());
//      output(base1.loginResponse);
//      drive.getAllHeadgear();
//      new MallBase(base).buy(type, id, packageId, num, price);
//      drive.getUserHeadgearInfo();
//      NajmBase.getUserBalance(base.user_id);
//      drive.getUserHeadgearInfo();
//      drive.getOnsaleHeadgear();
        int times = 0;
        while (true) {
            times++;
            int type = 1, id = getRandomInt(2) == 1 ? 34 : 36, packageId = id == 34 ? 56 : 60, num = 1, price = 1;
            long deadtime1 = drive.getHeadgearInfo().get(id);
            Verify verify = new Verify(new MallBase(base).buy(type, id, packageId, num, price));
            drive.getUserHeadgearInfo();
            long deadtime2 = drive.getHeadgearInfo().get(id);
            if (deadtime2 - deadtime1 != DAY) break;
        }
        output("一共进行了:" + times);
//      output(drive.getHeadgearInfo());
//      output(drive.usingHeadgear);
//      output(drive.loginKey);
//      output(drive.args);
//      output(base.loginResponse.getJSONObject(DATAINFO).getJSONObject("headGear").getInt("id"));
        testOver();
    }

    /**
     * 获取所有头套信息,包括下架的
     *
     * @return
     */
    public JSONObject getAllHeadgear() {
        String url = HOST + HeadgearApiPath.GET_ALL_HEADGEAR;
        HttpGet httpGet = getHttpGet(url);
        JSONObject response = getHttpResponseEntityByJson(httpGet);
        output(response);
        return response;
    }

    /**
     * 用户切换头套接口
     *
     * @param hid
     * @return
     */
    public JSONObject switchHeadgear(int hid) {
        String url = HOST + HeadgearApiPath.SWITCH_HEADGEAR + hid + changeJsonToArguments(args);
        HttpPost httpPost = getHttpPost(url);
        JSONObject response = getHttpResponseEntityByJson(httpPost);
//      output(response);
        return response;
    }

    /**
     * 获取用户头套信息
     *
     * @return
     */
    public JSONObject getUserHeadgearInfo() {
        sleep(1);
        String url = HOST + HeadgearApiPath.GET_USER_HEADGEAR;
        JSONObject response = getHttpResponseEntityByJson(getHttpGet(url, args));
        output(response);
        if (isRightResponse(response)) {
            headgearInfo.clear();
            JSONArray jsonArray = response.getJSONArray(DATAINFO);
            jsonArray.forEach(json -> {
                    JSONObject jsonObject = JSONObject.fromObject(json.toString()) ;
                    String name = jsonObject.getString("name") ;
                    long deadTime = jsonObject.getLong("deadlineTime") ;
                    int headgearId = jsonObject.getInt("goodId") ;
                    int use = jsonObject.getInt("isUse") ;
            if (use == 1) usingHeadgearId = headgearId;
            headgearInfo.put(headgearId, deadTime);
            output(name, headgearId, getTimeByTimestamp(deadTime));
        } );
        }
        return response;
    }

    /**
     * 获取在售的头套的列表
     *
     * @return
     */
    public JSONObject getOnsaleHeadgear() {
        String url = HOST + HeadgearApiPath.GET_ONSALE_HEADGEAR;
        JSONObject response = getHttpResponseEntityByJson(getHttpGet(url, args));
        output(response);
        return response;
    }

    public int getUsingHeadgearId() {
        getUserHeadgearInfo();
        return usingHeadgearId;
    }

    public Map<Integer, Long> getHeadgearInfo() {
        getUserHeadgearInfo();
        return headgearInfo;
    }

}

下面是基于该功能的自动化测试用例,main方法里面是调试用例的过程,执行用例的方法在之前的文章写过,利用反射去记录用例信息和执行测试用例,并保存测试结果,输出测试报告,异常预警等等:

/**
 * 用户0-10
 */
public class HeadgearCase extends SourceCode {
    static HeadgearCase headgearCase = new HeadgearCase();
    static NajmBase base = new NajmBase(V580User.getUserName(0));
    static Headgear drive = new Headgear(base);

    public static void main(String[] args) {
//      headgearCase.testDemo001();
//      headgearCase.testDemo002();
//      headgearCase.testDemo003();
//      headgearCase.testDemo004();
        headgearCase.testDemo005();
//      headgearCase.testDemo006();
        ApiLibrary.testOver();
    }

    /**
     * 获取所有头套信息用例
     */
    public void testDemo001() {
        String label = "获取所有头套信息用例" + TAB + Thread.currentThread().getStackTrace()[1];
        Verify verify = new Verify(drive.getAllHeadgear());
        JSONObject result = new JSONObject();
        result.put("状态码为0", verify.isRight());
        result.put("包含数组", verify.isArray("heads"));
        result.put("包含已下架的头套", verify.isContains("自动化专用3"));
        result.put("包含正在出售的头套", verify.isContains("自动化专用1"));
        MySqlTest.saveTestResult(label, result);
    }

    /**
     * 获取在售的头套用例
     */
    public void testDemo002() {
        String label = "获取在售的头套用例" + TAB + Thread.currentThread().getStackTrace()[1];
        Verify verify = new Verify(drive.getOnsaleHeadgear());
        JSONObject result = new JSONObject();
        result.put("状态码为0", verify.isRight());
        result.put("包含数组", verify.isArray("dataInfo"));
        result.put("不包含已下架的头套", !verify.isContains("自动化专用3"));
        result.put("包含正在出售的头套", verify.isContains("自动化专用1"));
        result.put("包含描述信息", verify.isContains("测试10天"));
        MySqlTest.saveTestResult(label, result);
    }

    /**
     * 获取用户头套信息用例
     */
    public void testDemo003() {
        String label = "获取用户头套信息用例" + TAB + Thread.currentThread().getStackTrace()[1];
        Verify verify = new Verify(drive.getUserHeadgearInfo());
        JSONObject result = new JSONObject();
        result.put("状态码为0", verify.isRight());
        result.put("用户头套正常", verify.isContains("自动化专用1"));
        result.put("用户佩戴正常", verify.isContains("\"isUse\":1"));
        result.put("头套套餐正常", verify.isContains("测试1天"));
        MySqlTest.saveTestResult(label, result);
    }

    /**
     * 余额不足购买用例
     */
    public void testDemo004() {
        String label = "余额不足购买用例" + TAB + Thread.currentThread().getStackTrace()[1];
        NajmBase base = new NajmBase(V580User.getUserName(1));
        int type = 1, id = 36, packageId = 60, num = 1, price = 1;
        Verify verify = new Verify(new MallBase(base).buy(type, id, packageId, num, price));
        JSONObject result = new JSONObject();
        result.put("状态码为35", 35 == verify.getCode());
        MySqlTest.saveTestResult(label, result);
    }

    /**
     * 正常购买用例
     */
    public void testDemo005() {
        String label = "正常购买用例" + TAB + Thread.currentThread().getStackTrace()[1];
        int type = 1, id = getRandomInt(2) == 1 ? 34 : 36, packageId = id == 34 ? 56 : 60, num = 1, price = 1;
        int balance = NajmBase.getUserBalance(drive.user_id);
        long deadtime1 = drive.getHeadgearInfo().get(id);
        Verify verify = new Verify(new MallBase(base).buy(type, id, packageId, num, price));
        drive.getUserHeadgearInfo();
        long deadtime2 = drive.getHeadgearInfo().get(id);
        int balance1 = NajmBase.getUserBalance(drive.user_id);
        JSONObject result = new JSONObject();
        result.put("状态码为0", verify.isRight());
        result.put("截止日期正确", (deadtime2 + EMPTY).equals(verify.getValue("deadlineTime")));
        result.put("头套日期增加正常", deadtime2 - deadtime1 == DAY);
        result.put("用户余额减少正常", balance - balance1 == 1);
        MySqlTest.saveTestResult(label, result);
    }

    /**
     * 用户切换头套用例
     */
    public void testDemo006() {
        String label = "用户切换头套用例" + TAB + Thread.currentThread().getStackTrace()[1];
        drive.getAllHeadgear();
        int id1 = drive.getUsingHeadgearId() == 34 ? 36 : 34;
        Verify verify = new Verify(drive.switchHeadgear(id1));
        int id2 = drive.getUsingHeadgearId();
        JSONObject result = new JSONObject();
        result.put("状态码为0", verify.isRight());
        result.put("头套切换成功", id1 == id2);
        MySqlTest.saveTestResult(label, result);
    }
}

下面是一个针对其中某个功能的性能测试用例(测试用例分两种,一类是HTTP单次请求的,我才用了获取请求的HttpRequestBase对象然后去重新发送并发请求,一类是多接口或者非HTTP请求,如dubbo,mysql,redis,消息队列等等,直接调用的方法进行压测):

简单HTTP请求:

class CancelReason extends OkayBase{

    public static void main(String[] args) {
        def argsUtil = new ArgsUtil(args)
        def thread = argsUtil.getIntOrdefault(0, 2)
        def times = argsUtil.getIntOrdefault(1, 5)

        def base = getBase()
        
        Headgear drive = new Headgear(base);
        drive.getAllHeadgear()
        def request = FanLibrary.getLastRequest()

        def timesthread = new RequestThreadTimes(request, times)
        new Concurrent(timesthread, thread,"获取所有头套,内容流转二期压测接口").start()

        allOver()
    }
}

非简单HTTP请求的请参考之前写过的性能测试框架Demo:性能测试框架第二版。这类方法写起来比较简单,使用范围很高,但是需要根据不同的业务需求解决多线程数据和对象的安全问题。

整个项目放在git上,功能测试在本地,自动化项目和性能项目在服务器,采用Groovy脚本运行,也可以在本地调试。自动化项目采取定时或者间隔固定时间自动运行,性能项目收到输入命令groovy filename.groovy来运行。


  • 郑重声明:文章首发于公众号“FunTester”,禁止第三方(腾讯云除外)转载、发表。

技术类文章精选

非技术文章精选

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

推荐阅读更多精彩内容