如何测试陌生的协议?

你工作中如果有类似的陌生协议(既可以是第一次接触的协议,也可以是企业私有协议),你是如何解决的呢?

那在面对一个陌生的新协议时,测试工程师的首要任务是什么呢?

在我看来,就是要测试接口的正确逻辑、错误逻辑是否满足最初的需求,因此,我们需要快速地掌握验证手段。

拿到开发工程师的客户端代码

面对一个陌生协议的接口测试任务时,无论如何,第一次你还是需要先拿到并了解开发工程师写的客户端代码,因为这样,你就可以对调用方式、参数等接口相关的一些内容有初步印象。在读完相关代码后,你就算是和这些接口完成了首次“会面”,下面你就要想办法敲开接口的大门,让自己能访问被测接口。

借助已有工具

如fiddler,能否查看协议信息。

自己写代码

Python 提供了 WebSocket 的协议库,因此我只要用它完成客户端的撰写,就可以进行接口测试了。这里,我写下了第一个 WebSocket 的调用代码


#引入websocket的create_connection类
from websocket import create_connection
# 建立和WebSocket接口的链接
ws = create_connection("ws://echo.websocket.org")
# 打印日子
print("发送 'Hello, World'...")
# 发送Hello,World
ws.send("Hello, World")
# 将WebSocket的返回值存储result变量
result = ws.recv()
# 打印返回的result
print("返回"+result)
# 关闭WebSocket链接
ws.close()

WebSocket 是一个长连接,因此需要人为的建立连接,然后再关闭链接,而 HTTP 却并不需要进行这一操作。 

将 WebSocket 接口封装进你的框架

封装了 Common 类,你可以在它的构造函数中,添加一个 API 类型的参数,以便于知道自己要做的是什么协议的接口,其中 http 代表 HTTP 协议接口,ws 代表 WebSocket 协议接口。由于 WebSocket 是一个长连接,我们在 Common 类析构函数中添加了关闭 ws 链接的代码,以释放 WebSocket 长连接。


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# python代码中引入requests库,引入后才可以在你的代码中使用对应的类以及成员函数
import requests
from websocket import create_connection


# 定义一个common的类,它的父类是object
class Common(object):
  # common的构造函数
  def __init__(self,url_root,api_type):
    '''
    :param api_type:接口类似当前支持http、ws,http就是HTTP协议,ws是WebSocket协议  
    :param url_root: 被测系统的根路由   
    '''    
    if api_type=='ws':
      self.ws = create_connection(url_root)
    elif api_type=='http':
      self.ws='null'
      self.url_root = url_root


  # ws协议的消息发送
  
  def send(self,params):
    '''
    :param params: websocket接口的参数
    
    :return: 访问接口的返回值
    ''' 
    self.ws.send(params)
    res = self.ws.recv()
    return res


  # common类的析构函数,清理没有用的资源
  
  def __del__(self):
    '''
    :return:
    '''
    if self.ws!='null":
       self.ws.close()
  def get(self, uri, params=None):
    '''
    封装你自己的get请求,uri是访问路由,params是get请求的参数,如果没有默认为空 
    :param uri: 访问路由 
    :param params: 传递参数,string类型,默认为None 
    :return: 此次访问的response
    '''
    # 拼凑访问地址
    if params is not None:
      url = self.url_root + uri + params
    else:    
      url = self.url_root + uri
    # 通过get请求访问对应地址
    res = requests.get(url)
    # 返回request的Response结果,类型为requests的Response类型
    return res
  def post(self, uri, params=None):
    '''
    封装你自己的post方法,uri是访问路由,params是post请求需要传递的参数,如果没有参数这里为空
    :param uri: 访问路由
    :param params: 传递参数,string类型,默认为None
    :return: 此次访问的response
    '''
    # 拼凑访问地址
    url = self.url_root + uri
    if params is not None:
       # 如果有参数,那么通过post方式访问对应的url,并将参数赋值给requests.post默认参数data
      # 返回request的Response结果,类型为requests的Response类型
      res = requests.post(url, data=params)
    else:
      # 如果无参数,访问方式如下
      # 返回request的Response结果,类型为requests的Response类型
      res = requests.post(url)    
    return res


  def put(self,uri,params=None):
    '''
    封装你自己的put方法,uri是访问路由,params是put请求需要传递的参数,如果没有参数这里为空
    :param uri: 访问路由
    :param params: 传递参数,string类型,默认为None
    :return: 此次访问的response
    '''
    url = self.url_root+uri
    if params is not None:
      # 如果有参数,那么通过put方式访问对应的url,并将参数赋值给requests.put默认参数data
      # 返回request的Response结果,类型为requests的Response类型
      res = requests.put(url, data=params)
    else:
      # 如果无参数,访问方式如下
      # 返回request的Response结果,类型为requests的Response类型
      res = requests.put(url)
    return res


  def delete(self,uri,params=None):
    '''
    封装你自己的delete方法,uri是访问路由,params是delete请求需要传递的参数,如果没有参数这里为空
    :param uri: 访问路由
    :param params: 传递参数,string类型,默认为None
    :return: 此次访问的response
    '''
    url = self.url_root + uri
    if params is not None:
      # 如果有参数,那么通过put方式访问对应的url,并将参数赋值给requests.put默认参数data
      # 返回request的Response结果,类型为requests的Response类型
      res = requests.delete(url, data=params)
    else:
      # 如果无参数,访问方式如下
      # 返回request的Response结果,类型为requests的Response类型
      res = requests.put(url)
    return res

 


from common import Common
# 建立和WebSocket接口的链接
con = Common('ws://echo.websocket.org','ws')
# 获取返回结果
result = con.send('Hello, World...')
#打印日志
print(result)
#释放WebSocket的长连接
del con

我的尝试的一般顺序:现成工具,工具现成插件,测试框架+项目组封装或采用的api库(如:junit +对应的协议开源库)。 个人觉得遇到这种情况,要会点编程,孰练测试框架胜算才大ʘᴗʘ 工作中有突然遭遇xmpp和mqtt协议性能测试的经历,最终jmeter+开源插件+厂商client jar包+demo+api手册搞定了任务。

年前刚好公司年会,然后开发了个聊天小程序,就给我测。当时完全不知道怎么搞,就去看这个协议。然后就想先抓包,结果抓不到,捣鼓了修改fiddler抓到了包,然后也是看抓到的数据好像跟平时不一样,然后各种搜索,平时使用的jmeter,就在jmeter中按往常的来试试,正常取样器不行,最后看到jmeter中有个websocket取样器,开心完成压测。

像微服务接口,一般都是使用特殊协议如dubbo、protobuf进行通信的,这种情况应该怎么做测试呢?只能用自己擅长的编程语言写一个客户端模拟调用来进行测试?这种还需要连接配置中心什么的。而且会不会存在有些协议是不跨语言的,比如仅限Java语言,不支持python。

websocket协议也是我遇到的第一个陌生协议,那个时候测试工期短,用的是java技术栈,就用了netty框架来封装,后面有时间了就进行回顾,发现还有有个websocket.jar开源包,后面又相继遇到了amqp、mqtt等非http协议!

针对一个陌生协议的第一次接口测试,你要保持自己敏锐的测试嗅觉,依据自己的技术基础,尽快解决问题。总地来说,你可以通过三步快速完成测试任务:

1、借力开发工程师。你首先该借力就是开发工程师,但你不要进入开发工程师给你的那种,从技术基础和理论开始学起,再逐步应用的学习脉络。你要一击致命,直接把他的客户端代码拿来,尽最大可能挪为己用,将其变成自己的接口测试代码。

2、站在自己的技术栈之上,完成技术积累。如果开发工程师的代码并不能拿来使用,那么你就需要站在自己的技术栈上寻求解决方法,这其中既包含了你已经熟悉的测试工具、测试平台,也包含了自己的测试框架和编码基础。

3、归入框架。无论你使用哪一种方法,在完成测试工作后,你还是要掌握对应的理论基础,同时想办法将这个一开始陌生的接口,通过自己熟悉的方式合并到你自己的框架中,不断扩充自己框架的测试能力,不断丰富你自己的测试手段。

你可能感兴趣的