【接口自动化平台搭建】TestNG搭建接口自动化(二)

上节内容传送门 https://blog.csdn.net/weixin_44025411/article/details/122780210
上节主要讲了如何进行收集测试后的结果信息

TestCase基类的设计思路

  • 我们在测试的过程中,“造数据”是不可避免的。

比如我们需要测试一个动态的点赞功能,那我们就需要造出来多种类型的“动态”,图文、视频,文字长短、视频长短各种各样的。发动态的接口,我们又是必须要测试的,因此,如果可以在写这个接口的时候,顺带把“造动态”的功能涵盖进去,就完美了。

  • 我们需要多维度的数据。

比如,针对某一个服务的接口通过率;总api覆盖率;某一个测开人员的自动化用例数量;甚至是断言与用例比等等;

  • 多套测试环境,如何一键切换环境

这个解决思路有多种,可以写进代码里,写好切换函数,一键切换;也可以配置到Jenkins参数中,进行动态配置;此处我采用的方法是使用Apollo配置中心进行统一管理。也可以借助配置中心进行工具类自动装配。

一、TestCase第一层基类 —— 模板设计&工具类实例的自动装配与销毁

话不多说,上代码

package com.base.test.tng.framework;

import cn.hutool.core.util.StrUtil;
import com.base.test.tng.framework.entity.ApiGan;
import com.base.test.tng.framework.entity.BulletMes;
import com.base.test.tng.framework.entity.ResultMes;
import com.base.test.tng.tools.OmnipotentTool;
import org.testng.SkipException;
import org.testng.annotations.Test;

/**
 * @Description: 设计case的模板流程
 * @Author: Joe Throne
 * @CreatedTime: 2022-02-05 16:56
 * 模板设计模式,测试一个case的过程中 必要的对象,创建和销毁
 */

public abstract class BaseQTNGCase {
    protected ApiGan apiGan = null;
    protected ResultMes resultMes;
    protected OmnipotentTool omnipotentTool;

    public final ResultMes fire(BulletMes bulletMes){
        onStart();
        beforeRun(apiGan,bulletMes,omnipotentTool);
        resultMes = howToShot(apiGan,bulletMes,omnipotentTool);
        afterRun(apiGan,bulletMes,omnipotentTool,resultMes);
        onFinish();
        return resultMes;
    }

    /**
     * 1、将工具类进行实例化
     * 2、将必要的参数进行初始化,如:url、服务信息等填写
     */
    protected void onStart(){
        apiGan = initGan();//先初始化枪杆
        if(apiGan == null || StrUtil.isEmpty(apiGan.getHost())){
            //如果apiGan没有初始化,或者主机地址为空字符串,抛出跳过异常
            throw new SkipException("apiGan没有初始化");
        }
        omnipotentTool = new OmnipotentTool(apiGan);//将枪杆中信息给到万能工具类
    }

    protected void beforeRun(ApiGan apiGan,BulletMes bulletMes,OmnipotentTool omnipotentTool){}

    protected abstract ResultMes howToShot(ApiGan apiGan,BulletMes bulletMes,OmnipotentTool omnipotentTool);

    /**
     * 必须要让最终case进行继承并实现
     * @return
     */
    protected abstract ApiGan initGan();

    protected void afterRun(ApiGan apiGan, BulletMes bulletMes, OmnipotentTool omnipotentTool,ResultMes resultMes){}

    private void onFinish() {
        apiGan = null;
        omnipotentTool.destory();//摧毁万能工具类中的所有对象
        omnipotentTool = null;
    }
}

核心方法是以下几个:

onStart();
beforeRun(apiGan,bulletMes,omnipotentTool);
howToShot(apiGan,bulletMes,omnipotentTool);
afterRun(apiGan,bulletMes,omnipotentTool,resultMes);
onFinish();
  • onStart():将Gan方法回调的信息进行全局初始化,并使用apiGan作为参数给到万能工具类OmnipotentTool,万能工具类内部再次进行传递,根据服务信息从Apollo上获取对应的信息,实例化OmnipotentTool内部的mq/redis/mysql/http/es等工具类
  • beforeRun():在case执行前的一些筹备工作,如,调用其他依赖的接口
  • howToShot():形参中都是已经实例化的对象,可以直接拿来使用,具体的case执行方案,利用本函数内的形参信息中进行获取。抽象方法需要子类进行实现
  • afterRun():执行完case后的操作,如:将数据库等信息还原
  • onFinish():将当前的万能工具类销毁

二、TestCase第二层基类 —— 断言的简单封装

这个很简单,没什么可说的。
就是在case执行时,如果调用封装出来的断言,就会维护一个计数器,将断言计数+1

package com.base.test.tng.framework;

import org.testng.Assert;
/**
 * @Description: 这一层主要做自定义断言,方便为断言数量进行计数
 * @Author: Joe Throne
 * @CreatedTime: 2022-02-05 22:05
 */

public abstract class BaseQTNGAssertCase extends BaseQTNGCase{
    protected int methodAssertNum;

    protected void AssertQTNGEquals(Object object,Object object2){
        methodAssertNum ++;
        Assert.assertEquals(object,object2);
    }

    protected void AssertQTNGEquals(String s1,String s2){
        methodAssertNum ++;
        Assert.assertEquals(s1,s2);
    }

    protected void AssertQTNGEquals(int code1,int code2){
        methodAssertNum ++;
        Assert.assertEquals(code1,code2);
    }

    protected void AssertQTNGNotEquals(int code1,int code2){
        methodAssertNum ++;
        Assert.assertNotEquals(code1,code2);
    }

    protected void AssertQTNGNotEquals(String s1,String s2){
        methodAssertNum ++;
        Assert.assertNotEquals(s1,s2);
    }
}

三、TestCase第三层基类 —— Case执行后的信息收集

package com.base.test.tng.framework;

import com.base.test.tng.report.Constant;
import com.base.test.tng.report.entity.ResultMethodDetailEntity;
import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 直接被case继承的基类
 * @Author: Joe Throne
 * @CreatedTime: 2021-12-19 1:54
 * 1、自定义断言,便于统计当前case中的断言数量,便于对比断言和case比例。
 * 2、对一个class执行的时间进行计时,针对时间较长的进行拦截并跳过
 */

public abstract class BaseTestCase extends BaseQTNGAssertCase{


    @BeforeMethod
    public void qngBeforeMethod(){
        //将断言计数器进行初始化
        methodAssertNum = 0;
    }

    /**
     * 1、如果当前Case超时,就直接判定为执行失败。修改Mes为失败
     * 2、收集当前Case的apiGan信息,用于后期的统计
     * 3、收集当前Case运行过后的methodAssertNum信息,用于后期的统计
     */
    @AfterMethod(alwaysRun = true)
    public void qngAfterMethod(ITestResult testResult){
        testResult.setAttribute(Constant.CASE_GAN,apiGan);  //将api枪杆进行收集
        if(testResult.getStatus() == ITestResult.SUCCESS){
            //case执行成功了,还需要判断它的执行时间是否达标
            boolean isOverTime = testResult.getEndMillis() - testResult.getStartMillis() > Constant.CASE_MAX_TIME;
            if(isOverTime){
                //如果当前Case超时
                testResult.setStatus(ITestResult.SKIP);
                testResult.setThrowable(new SkipException("Case超出自定义预期时间"));
            }
        }
        testResult.setAttribute(Constant.METHOD_ASSERT_NUM,methodAssertNum);
    }
}

四、TestCase编写Demo

package com.base.test.tng.testcase.server1;

import com.base.test.tng.framework.BaseTestCase;
import com.base.test.tng.framework.QTNGTestAnn;
import com.base.test.tng.framework.entity.ApiGan;
import com.base.test.tng.framework.entity.BulletMes;
import com.base.test.tng.framework.entity.ResultMes;
import com.base.test.tng.tools.OmnipotentTool;

import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description:
 * @Author: Joe Throne
 * @CreatedTime: 2021-12-19 1:49
 * 为了使case日后有更大的拓展性,要求:
 * 1、howToShot中完全使用形参自动注入的内容进行
 * 2、测试类中,需要构建出一个,子弹消息,调用fire
 * 3、通过fire获得的返回值中进行断言
 * 4、一个类中允许有多个case,但是不建议(只是不建议而已)有多个api使用
 */

public class TestCase1 extends BaseTestCase{
    //case中不需要全局变量

    @Override
    protected ResultMes howToShot(ApiGan apiGan, BulletMes bulletMes, OmnipotentTool omnipotentTool) {
        //此处是具体编写测试逻辑的地方
        //使用ApiGan中的host和url等信息,再加上万能工具类omnipotentTool,进行http或rpc或mq或db操作,完成测试链路
        //在获得请求返回结果后,封装为ResultMes对象进行返回。
        //编写时需要注意,如果有一些sql等,需要使用形参进行拼接
        return null;
    }

    @Override
    protected ApiGan initGan() {
        //此处填写服务和api相关信息
        //此处写的信息将会直接装填到howToShot的形参中
        return new ApiGan("server1","http://localhost:8090","/user/login");
    }

    @Test
    public void testLogin(){
        BulletMes bulletMes = new BulletMes();  //初始化子弹消息
        bulletMes.setUsername("joethrone");     //如果需要登录信息,交给HTTP请求的工具类进行完成
        bulletMes.setUsername("qwerqwer");
        ResultMes resultMes = fire(bulletMes);
        AssertQTNGEquals(200,200);              //使用父类的断言,便于最终进行断言数量收集
        AssertQTNGEquals(200,201);              //遇到一个失败的断言,后续的就不会再执行了,断言数量的统计也就到此为止了
    }
}

预留拓展点:

当接口自动化引入web依赖,做web服务之后,每一个TestCase,只需要外部传入一个BulletMes,就可以变成一个“造数场景”。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李易安QSR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值