异步接口测试

一、异步接口

1、什么是同步接口,什么是异步接口?

同步接口:当你发送一个接口请求信息给其他系统,其他系统会同时返回一个处理的结果(status_code,msg,如果查询类的返回查询结果)
是所有的操作都做完,才返回给用户结果。即写完数据库之后才响应用户,用户体验不好,有种卡死的感觉。
这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。

异步接口:有些是因为某个接口业务需要较长时间的处理,为了不影响其他的功能,被调用系统在校验字符串符合要求后,直接返回一个简单的结果(比如一个id信息)供后续查询结果
不用等所有操作等做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好,没有种卡死的感觉。

"同步" 就好比:你去外地上学(人生地不熟),突然生活费不够了;此时你决定打电话回家,通知家里转生活费过来,可是当你拨出电话时,对方一直处于待接听状态(即:打不通,联系不上),为了拿到生活费,你就不停的 oncall 、等待,最终可能不能及时要到生活费,导致你今天要做的事都没有完成,而白白花掉了时间。
 
"异步" 就好比:在你打完电话发现没人接听时,猜想:对方可能在忙,暂时无法接听电话,所以你发了一条短信(或者语音留言,亦或是其他的方式)通知对方后便忙其他要紧的事了;这时你就不需要持续不断的拨打电话,还可以做其他事情;待一定时间后,对方看到你的留言便回复响应你,当然对方可能转钱也可能不转钱。但是整个一天下来,你还做了很多事情。或者说你找室友临时借了一笔钱,又开始 happy 的上学时光了。
 
 简而言之,言而总之: 同步就是我强依赖你(对方),我必须等到你的回复,才能做出下一步响应。即我的操作(行程)是顺序执行的,中间少了哪一步都不可以,或者说中间哪一步出错都不可以,类似于编程中程序被解释器顺序执行一样;同时如果我没有收到你的回复,我就一直处于等待、也就是阻塞的状态。   异步则相反,我并不强依赖你,我对你响应的时间也不敏感,无论你返回还是不返回,我都能继续运行;你响应并返回了,我就继续做之前的事情,你没有响应,我就做其他的事情。也就是说我不存在等待对方的概念,我就是非阻塞的。

2、异步接口应用场景

当业务处理比较耗时, 一般会采用异步处理的方式, 这种异步处理的方式又叫Future模式。

3、异步接口一般流程

1)提交请求接口
当你请求一个异步接口,接口会立刻返回一个结果告诉你已经开始处理,结果中一般会包含一个任务id类似的东西用于追踪结果。

2)查询结果接口
另外会提供一个查询结果的接口, 当结果未处理完查询接口会返回相应的"未完成"状态,如果已经处理完,则会返回相应的数据。

4、异步接口处理方法

1)一般采取轮询的方法,每隔一定时间间隔去请求一下查询结果的接口,直到接口返回的状态是已完成(查询到指定数据或超时)。

2)如果异步接口没有提供追踪id和查询接口,可以通过同样的方法轮询查取数据库数据或日志数据直到获取到指定结果或超时。

二、案例业务场景

1、案例业务场景介绍

  1. 用户申请退订商品请求。
  2. 平台收到申请,返回一个退订流程id号给用户。
  3. 平台进行处理申请。
  4. 用户拿这个id去查询之前申请的退订商品业务。

2、技术方案

  1. 开发直接增加该功能,测试来验证(缺点:周期比较长,任务紧)。
  2. 测试人员使用一个简易的mock技术,验证自身的自动化脚本,调通。

3、取对应的接口详情

1、 退订商品申请接口
	请求地址    /order/create/
	请求方法    POST
	请求格式    Json

	参数          类型         说明
	user_id     String      用户id
	goods_id    String      商品id
	num         int         数量
	amount      float       总价

	响应示例
	缺少参数:
	{
	    "msg": "参数缺失"
	}

	成功:
	{
	    "order_id": "111"
	}
#==================================
2、查询退订商品结果接口
	请求地址    /get_result/
	请求方法    GET

	参数          类型      说明
	order_id    String    订单id

	响应示例
	创建中:    {}
	创建成功:
	{
	    "user_id": "abc",
	    "goods_id": "123456",
	    "num": 1,
	    "amount": 100.0
	}

4、技术实施

   1、申请的接口
   2、查询接口
   josn文件内容如下:
   """
   [
   {
       "description":"1-创建订单接口", 
       "request":{
           "uri":"/order/create/",
           "method":"POST",
           "headers":{
                       "Content-Type":"application/json"
                   },
           "json":{
                       "user_id": "abc",
                       "goods_id": "123456",
                       "num": 1,
                       "amount": 100.0
                   }
           },
       "response":{
           "status":200,
           "headers":{
                   "Content-Type":"application/json"
                   },
           "json":{
                   "order_id": "111"
               }
       }
   },
   {
       "description":"2-查询订单接口",
       "request":{
           "uri":"/get_result/",
           "method":"GET",

           "queries":{
                       "order_id": "111"
                   }
           },
       "response":{
           "status":200,
           "headers":{
                   "Content-Type":"application/json"
                   },
           "json":{
                       "user_id": "abc",
                       "goods_id": "123456",
                       "num": 1,
                       "amount": 100.0
               }
       }
   }
   ]
   """
   3、启动Mock

三、编写Python代码

import time
import requests
import threading  # 多线程模块

HOST = "http://127.0.0.1:9090"

def create_order():
    """
    提交退订请求 接口
    :return: "order_id"的值: "111"
    """
    url = f"{HOST}/order/create/"
    payload = {
        "user_id": "abc",
        "goods_id": "123456",
        "num": 1,
        "amount": 100.0
    }
    response = requests.post(url, json=payload)
    # print(response.json())
    # print(response.json()['order_id'])
    # return response.json()
    return response.json()['order_id']

# if __name__ == '__main__':
#     create_order()
#     输出:
#     {'order_id': '111'}
#     111


def get_order_result(orderID, interval=3, timeout=20):
    """
    查询接口:查询申请退订商品的结果,使用请求接口的返回id去查询
    :param orderID:  订单id 111
    :param interval: 频率/s
    :param timeout:  超时时间/s,如果在超时时间内,查到结果就不需要继续查询!
    :return:
    """
    url = f"{HOST}/get_result/"
    payload = {"order_id": orderID}
    # 1、开始时间,当前时间
    startTime = time.time()
    # 2、结束时间
    endTime = startTime + timeout
    # 3、选择循环
    # while   靠条件结束
    # for     知道循环次数或遍历操作
    cnt = 0   # 计数变量
    while time.time() < endTime:
        response = requests.get(url, params=payload)
        cnt += 1
        if response.text:  # 有响应数据就结束循环!
            print(f"第{cnt}次查询,已经有查询结果>>> ", response.text)
            break
        else:
            print(f"第{cnt}次查询,没有结果,请稍等...")
        # 4、设置频率
        time.sleep(interval)  # 间隔多久运行一次
    print("查询完成")
    return response.text


if __name__ == '__main__':
    # 1、获取id
    id = create_order()
    print(f'查询id:{id}')
    # 2、查询结果
    # 方式1:单线程(在异步查询等待3s的时间里没有去执行其他模块接口用例)
    # response = get_order_result(id)
    # print(response)

    # 方式2:多线程(在异步查询等待3s的时间里可以去执行其他模块接口用例)
    """
    t1 = threading.Thread(target,args)  创建线程方法
    target                   作为子线程的函数
    args                     这个函数的实参
    """
    # 创建线程方法
    t1 = threading.Thread(target=get_order_result, args=(id,))
    # 设置守护线程  主线程(整个.py文件)退出了,子线程get_order_result 也退出
    # 设置为 True
    t1.setDaemon(True)
    # 启动线程
    t1.start()
    """
    扩展知识点:多线程技术
    并发:1个线程在跑,多线程之间来回切换。
    并行:每1个线程都是独立的,互不干涉的。
        1、io密集型   阻塞
            sleep()  requests库
        2、cpu密集型  计算型,这个多线程不一定省时间。
    """
    for one in range(10):
        time.sleep(1)
        print(f'{one}-----我正在执行其他模块的自动化测试----')


# 输出:
# 查询id:111
# 第1次查询,没有结果,请稍等...
# 0-----我正在执行其他模块的自动化测试----
# 1-----我正在执行其他模块的自动化测试----
# 2-----我正在执行其他模块的自动化测试----
# 第2次查询,没有结果,请稍等...
# 3-----我正在执行其他模块的自动化测试----
# 4-----我正在执行其他模块的自动化测试----
# 5-----我正在执行其他模块的自动化测试----
# 第3次查询,没有结果,请稍等...
# 6-----我正在执行其他模块的自动化测试----
# 7-----我正在执行其他模块的自动化测试----
# 8-----我正在执行其他模块的自动化测试----
# 第4次查询,没有结果,请稍等...
# 9-----我正在执行其他模块的自动化测试----
posted @ 2023-01-05 15:21  ll=ll  阅读(1271)  评论(0编辑  收藏  举报