聊一聊游戏的接口测试落地

发表于:2016-12-20 08:05

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:jianjianjianbing    来源:51Testing软件测试网采编

  什么是游戏的接口测试
  接口测试很官方的定义,上网一搜就有很多资料了。对于游戏而言,简单来说,接口就是服务端和客户端通讯请求的约定,客户端告诉服务端,我要做什么(操作协议号),怎么做(参数列表)。
  举个栗子,升级技能,服务端和客户端有个约定:升级技能的操作协议号是110101,参数是技能的itemID(int类型),玩家点击了升级A技能(itemID:10001),客户端就发送包含协议号110101,参数为10001的封包到了服务端。
  游戏接口测试的必要性
  接口测试要不要做,就举两个如果没做接口测试,有可能会出现的bug吧:
  · 重发领奖封包,可以重复领奖。
  · 背包出售道具,修改售价溢出,获取大量游戏币。
  接口测试的落地
  这是游戏和APP很大的一个不同点。大部分的APP采用的通讯协议是公有协议,如HTTP。标准化的,成熟的协议,有不少测试框架和工具可以直接选择使用。
  而游戏就略尴尬了。大部分都是私有协议,Socket通讯,封包结构自定义,数据采用二进制压缩传输,如Protocol Buffer。在工具的选择上,就没有APP那样百花齐放了。
  有些团队会使用WPE,WPE是一个经典的网络封包编辑器,可以拦截,修改,重发Socket协议的封包,对于爱折腾的游戏玩家,是必备工具。操作简单,入门教程上网一搜就不少。但是使用起来,也不太方便。
  · 对于二进制加密传输封包,WPE拦截到的封包,可读性不佳,乱码一团,修改封包的指定字段的数值也比较麻烦。
  · 发送封包后,没有提供返回结果的显示,操作是否生效只能在游戏内确认。
  · 虽然可以把发送过的封包保存起来,但是作为测试用例来统一管理是挺不方便的。
  所以在经历的几个项目中,最终都是使用了内部开发的接口工具,而每个项目的接口工具的原理和使用方式区别还挺大,在此分享下。
  项目1
  刚刚从学校踏入测试坑那会,项目是一个SLG页游,服务端的主程MM在内网游戏服务器上开启一个Web服务,可以接收HTTP Get请求,指定格式如下:
  http://192.168.22.248/sftx/gameSocket/send?u=playerId&c=protocol&p=port&params=param1|param2|param3
  参数的含义如下:
  u:玩家ID
  c:操作协议号
  p:服务器端口号
  params:参数列表,多个参数使用"|"连接
  举个栗子:调用玩家升级主城协议
  玩家ID:7001
  操作是升级主城:协议号110001
  参数列表:主城建筑ID是1001
  内网端口号:默认5001
  所以相应的Get请求是
  http://192.168.22.248/sftx/gameSocket/send?u=7001&c=110001&p=5001&params=1001
  Web服务器接收到Get请求后,会解析出相应的玩家ID,操作协议,参数列表,自动开启一个Socket连入游戏服务器,执行相应的操作,并返回处理结果。
  基于这个接口,可以对操作请求进行批量的新建,修改,发送,并解析返回结果。也因此实现了一些自动化的脚本,如批量建号,批量升级等,对工作效率的提升也是很明显的,比如新建军团后,跑个批量申请入团,军团就满人了。
  当时的不足之处:发送请求后,因为Web那边会自行创建新的Socket连接,会自动挤号。这一点如果可以进行优化的话,就更好用了。
  项目2
  项目2是一个RPG页游,前后端使用Socket通讯,数据交互格式是AMF3。
  当时后端底层在重写ing,所以没空折腾一个Web端口给我调用~这个时候前端主程FF站了出来,提出了一个方案。
  游戏在网页上加载的时候,同时也加载一个测试用的js文件。
  执行接口测试的方式,是在Chrome的console窗口,输入已经加载的js函数sendCommand,把操作内容作为函数的参数,回车运行后发送给Flash客户端,Flash客户端接收后,解析出相应的操作ID和参数列表,执行后在console窗口打印出服务端返回结果。
  函数调用格式如下:
  sendCommand(“PackagesController”, “move”, 0, [“BACKPACK”,0,“BACKPACK”,30]);
  操作模块:PackagesController  背包模块
  操作行为:move   移动背包物品
  参数列表:[“BACKPACK”,0,“BACKPACK”,30]   从第0个格子移动到第30个
  这个模式的一个好处:接口测试的请求是前端解析后,自行发出的,所以不会被挤号。
  项目3
  项目3是一个回合制的RPG手游,客户端使用Unity,Socket通讯,数据交互格式是PB,这次开始自己尝试独立完成接口工具,工具需求规划如下:
  · 支持录制请求
  · 可以对录制的请求进行复制,修改,删除
  · 解析请求里边的参数列表
  · 查看服务端的返回结果
  · 自动校验返回结果
  · 测试用例保存到本地
  实现过程中的一些积累在此记录下:
  录制的原理
  点击一个技能升级的按钮的背后发生了什么?
  1、UI接收到点击请求,调用技能模块
  2、技能模块准备好参数列表,调用Server层的Send方法,生成一个请求
  3、Server层接收后,对请求进行封装,加入校验key和请求头,压缩为PB格式,生成最终请求
  4、发送给服务端
  那么,要从哪里切入来录制请求呢?最终选择了在Server层接收后,对请求进行封装前,主要原因是,接口测试主要关注参数的不合理修改后,服务端能否做出正确判断,可以不用关心校验Key等其他信息,对请求进行修改后,点击发送,直接调用Send方法,底层就会完成新的请求封装和发送。省代码啊~
  那么问题来了,如何录制? 一开始采用的方式,是直接在Send函数里边,嵌入了转存请求的代码,但是这个做法并不合理,因为已经直接修改了开发的代码,下次从git更新代码,会有冲突,后来调整为前端底层提供一个onRequest的事件,我在需要转存请求的时候,就注册自己的事件处理函数。
public override void StartRecord() {
MsgCenter.AddMsg("Start to record Request");
EventHelper.Ins.Get<SystemEventGroup>().onRequest.AddHandler(OnRequest);
}
void OnRequest(ServerService ss, Request req) {
IList<object> paramList = null;
if (req.ParamList.IsNotBlank()) {
paramList = req.ParamList;
}
this.Add(new TRequest(req.Protocol, paramList));
}
  转存请求的TRequest的定义
[Serializable]
public class TRequest : ICloneable {
public int protocol;
public IList<object> paramList;
public string des = "空描述";
public TRequest(int protocol, IList<object> paramList) {
this.protocol = protocol;
this.paramList = paramList;
}
public object Clone() {
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream);
}
}
  同样的,录制服务端的返回结果,也是类似的方式。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号