【游戏测试专题】游戏自动化测试指南—产品保障框架细致入微

这里无图片,请跳转看原文
原文:游戏自动化测试指南

所谓自动化测试,就是模拟手工测试步骤,通过执行程序语言编制的测试脚本自动地测试软件产品,自动地实施产品的单元测试、功能测试、负载测试或性能测试等,以保障产品的稳定性。

本文将从以下几个方面和大家进行探讨:

一、自动化测试框架的搭建

二、自动化测试用例脚本的编写

三、自动化测试的特殊性

四、自动化框架的其它应用

主要介绍自动化测试在功能测试方面的应用,其它方面仅简要介绍。

一、前言
1.1 什么是自动化测试

测试工作中,很大一部分需要依靠手工进行,对于游戏产品而言,手工测试有着不可替代的意义,可以在操作游戏的同时,直接观察到游戏产品的行为是否正常,玩法的趣味性等。但其所带来的人员开销也是必不可少的,况且不能保证100%不会出错。而自动化测试,可以将平时的一部分测试工作从手工中解放出来,用代码来代替人工执行,用以提高测试效率。但自动化测试也有它的适用范围,并不是万能的,也并不能完全代替传统的人工测试。如果一味地追求自动化,反而会适得其反。

功能测试方面,大部分MMORPG 游戏都适合使用自动化测试。自动化测试有其特定的应用场合:1.适用于流程比较稳定的场合,比如基本功能、主线任务、日常任务、系统类玩法等等,这类功能流程一旦确定,后期大改可能性较小,在一定程度上能避免自动化测试脚本反复迭代的工作,自动化最忌讳的就是需求不稳定,频繁的变更会导致自动化测试用例的维护成本直线上升。2.适用于流程比较复杂的系统或者手工成本太高,比如主线任务,所涉及到的任务类型多、整体流程繁琐,人工测试费时费力,寻找某些种类缺陷时效率并不高,可能覆盖不全或者忽略了部分重点,采用自动化测试的方法能很好地规避这类问题。3.适用于高可用性的场合,也就是尽量不会出错的场合,在游戏中,存在一些功能有一定的容错机制,比如弱网下的状态就不太适合采用自动化的方式来测试。4.需要频繁执行回归的测试,游戏的生命周期一般都比较长,通常会有多个版本持续发布,每次版本发布都会有大量的回归测试需求。当然如果是短期的一次性项目,从技术上讲自动化测试的可行性很高,但从投入产出比考虑通常并不建议使用自动化,比如像节日活动这类玩法。

自动化测试的重要性不言而喻,但自动化测试又无法解决所有问题。所以说完全依赖自动化是不可能的,但完全没有自动化也不行。在产品开发过程中,重度依赖人力进行回归是一件非常枯燥重复的工作。游戏的开发周期是一个高效开发、快速迭代的过程,游戏系统相对来说都比较复杂,面对如此快速开发的产品,自动化测试的意义主要体现在:1.无人执守,自动执行;2.回归测试中,可以快速验证版本的稳定性;3.避免重复测试;4.节省劳动力;5.监控版本的性能数据。通常情况下,自动化测试流程一旦形成,除了上班时间以外,可以充分利用下班后的空闲时间、空闲机器自动执行测试脚本,充分利用资源来保障产品质量,日后的测试工作便可以高效循环进行,这是产品长期回归测试的高效方法,可以避免在产品上线或交付的最后一刻,还深陷软件问题的泥潭中。

万物皆有阴阳两面,尽管自动化测试有诸多好处,当然也有它的劣势。至今仍有很多项目组自动化水平不高,主要有以下几个方面:1.对测试人员要求相对较高;2.自动化测试的开发、测试结果分析、测试脚本编写及维护有一定的维护成本;3.自动化测试用例也分“好”、“坏”,测试结果不一定100%准确。所以产品组做到多大程度“自动化”需要根据各个项目组的具体情况来决定,切不可盲目追求自动化。

二、自动化测试框架的搭建
自动化测试框架,可以理解为,驱动所有测试用例持续执行的一套流程。这套流程从项目组打包就开始了,直到产出测试报告,全程由代码自动执行。自动化测试框架,可以由项目组内自己搭建,也可以借助合适的第3 方工具完成。接下来会介绍这两种不同的方式。

如果由项目组内搭建,测试框架的灵活性、可扩展性更高,不过对组内人员的代码要求也更高。若借助第3 方工具,比如网易自研的Airtest Project,可以在短期内快速搭建一套完整的测试框架,在用例的编写上,对测试人员的代码要求相对较低,可以脱离项目组的产品代码独立存在。至于采用哪种方式,可以根据各个项目组的人员配置、项目组的具体情况来决定。一般来说,如果是项目组内编写的框架,分为这样几部分:外围流程搭建、服务器框架搭建、客户端框架搭建。如果使用第3 方工具,参考官方文档即可。

2.1 外围流程框架搭建

外围流程,是指与测试用例的具体执行无关,但又是自动化测试过程中必不可少的步骤,比如测试包的下载、安装、启动、进入游戏、用例日志的收集、上传、分析、报告的生成、发送等等。一般来说,包括如下步骤:

图2.1 外围流程图
上述流程图,称之为自动化测试的外围流程,与用例执行不同,外围流程不需要依赖产品代码,属于纯人力操作转为代码执行的过程。该流程可以使用各自擅长的语言来开发,只要能保证整一套流程完整运行下来就可以。每一步操作大致如下:

1)检查build 版本是否完成

build 版本结束后,有包体才能开始测试,所以第一步需要检查build 版本是否完成。一般来说,项目组里的打包,同一个版本,服务器包会更早完成,客户端的包会晚一些,当然,特殊项目组、特定打包除外。因此,与服务器包相关的操作可以优先进行,待客户端出包后,再进行客户端相应操作。

2)部署自动化测试的服务器

服务器包打好后,代表可以进行服务器的部署了,客户端包打好后,代表可以进行客户端包的相关操作。在进行服务器部署时,有的项目组可能需要去下载相应的服务器包,再部署自动化测试服务器,部署好后再启动服务器。有的项目组,服务器包的下载、服务器版本的部署、启动服务器等功能绑定在一起,这几步操作可以一起执行,直到服务器成功启动。

3)下载客户端并解压

待客户端打包结束后,会将包放在特定的目录下,这时根据服务器的版本号,找到对应客户端包体,下载、解压缩即可。

4)启动客户端进入游戏

待客户端包体下载后,如果用例要运行在手机端,还需要进行包体的安装,后面第四部分会介绍手游自动化测试的差异性。

接下来,启动客户端,进行登陆操作,进入服务器。登陆操作,根据不同项目组产品代码架构的特点,会有些许差异。例如有的项目组,在进到登陆界面时,客户端和登陆进程的连接就会建立,此时登陆操作可以通过客户端产品代码UI 结构上的点击、输入等完成登陆操作,也可以直接调用登陆相应的接口;而有的项目组,在初始的登陆界面,客户端和登陆进程并没有建立连接,也就无法通过服务器向客户端发送协议的方式来完成登陆,这种情况下,可以通过增加额外客户端登陆代码来实现登陆。与此同时,外围流程会增加代码替换操作,将新增的客户端代码进行打包再替换。或者另辟蹊径,将登陆绑定在一个特定的操作上来实现。只要能实现登陆自动化,无论采取哪种方式都是可行的。

5)执行自动化测试用例

成功登陆,进入游戏后,可以开始执行用例了,这部分会在接下来框架搭建里详细介绍。

6)收集用例日志、分析、生成报告

自动化测试用例执行过程中,会产生一些日志,这里的日志,包括文本日志、游戏截图、游戏录制视频等等。由于自动化测试用例的执行过程无人执守,可以通过分析用例执行过程中生成的特定日志来判定一个用例是否运行成功。

有了日志分析结果后,需要将分析结果展示给项目组的同学,让大家知道当前版本运行用例的情况,结果展示要求尽可能做到清新、简洁、明了。在项目组上班之前给出当天要用版本的报告,以便在第一时间发现版本中的问题。目前雷火这边,由测试日志生成测试报告的过程,可以由各个项目组根据具体情况来决定格式和展示方式,不过为了数据展示的美观和统一,也可以借助MTL 或者平台组来设计相应的展示界面。下图2.2 为倩女手游项目组的日常自动化测试报告,报告里会展示出测试时间、测试的版本号、测试平台、用例总数、总的运行时间等信息,并且会详细列出每个用例的具体执行情况。

图2.2 自动化测试报告
外围流程基本不涉及和产品代码相关的内容,接下来会详细介绍框架的搭建,如果项目组内开发框架,可以参考2.2、2.3 两部分;如果使用第3 方工具Airtest Project,可以参考2.4 小节。

2.2 项目组内框架搭建

组内开发自动化测试框架一般分为两部分:服务器和客户端,服务器端为驱动引擎,用来控制、调配用例;客户端一方面作为两个用例间的连接桥梁,另一方面,需要实现用例在客户端执行期间可能涉及到的功能需要。

2.2.1 服务器框架搭建

为便于理解,以倩女服务器架构为例来介绍服务器框架,下图2.3 为倩女服务器的简易架构,先简单介绍倩女服务器的各个服务器进程:

master 进程:中央调度进程,负责整组服务器集中控制,每组服务器只有唯一一个master进程,主要提供全局信息的检索和管理。鉴于master 进程这些特性,会将用例的管理、调度、分配等放到master 进程上来处理,避免多进程下状态异步的问题。

gas 进程:负责处理具体的服务器端游戏逻辑,会与master、DB 等进程保持连接,通常一组服务器会有多个gas 进程。用例具体的执行逻辑会放到gas 进程上,通过gas 进程,将用例脚本发送给相应的客户端(gac)执行。

gac 客户端:游戏客户端,比如手机、PC 等等,玩家操作都是在客户端,所以为了更真实地模拟人工测试,通常测试脚本会在客户端执行。

图2.3 倩女服务器简易架构
用例的驱动过程:当一个新客户端gac1 登陆进入游戏,gac1 会向gas 进程发送请求,表示gac1 现在空闲,可以执行用例。gas 收到gac1 的请求后,会向master 发送请求,表示gac1 现在空闲,请求执行用例。master 进程收到请求后,在本进程上查询用例状态,找出下一个需要执行的用例,并将用例名返回给gas 进程。gas 进程拿到用例名后,将测试用例脚本发送到gac1 的客户端执行。同样地,当一个客户端执行完用例后,依然会向gas 进程发送请求,表示我现在空闲,可以执行用例,以此循环,直到所有用例全部执行。

框架搭建初期,可以使用一个客户端,逐个执行用例。随着用例积累,整体用例运行时间会逐渐增加,以倩女手游为例,如果使用一台机器逐个执行用例,所有用例一轮跑完需要50 小时左右,而每天至少会对一个日常版本进行自动化用例回归,总时长显然无法接受。所以在框架设计之初,最好能提前考虑多客户端并发执行。目前倩女手游这边,每次会启动10 个客户端同时执行用例,能大大缩短测试时长,让项目组同学尽早看到测试报告。

2.2.2 客户端框架搭建

自动化测试用例的脚本,可以运行在服务器端,也可以运行在客户端。运行在服务器端的脚本,主要是通过接口来测试,从客户端发起的这部分逻辑验证会被忽略。针对功能验证的自动化测试用例,更建议用例脚本运行在客户端,通过模拟用户操作的行为来测试。

编写测试用例之前,客户端需要提前实现以下几部分功能:其一,日志系统;其二,模拟用户操作的各类接口;其三,性能数据采样接口。

一般来说,游戏产品会有自己的日志系统,产品的日志系统更多用来服务游戏产品本身,进行信息记录以及BUG 定位等,通常不太适合用来分析测试用例。根据自动化测试用例的特点,自己开发相应的日志系统,测试用例的日志系统不用太复杂,轻量级即可。为了能更有效分析用例运行情况,回放出错用例执行过程,日志系统会包括文本日志、报错日志、游戏截图、游戏视频等等。文本日志用来记录用例运行期间产生的有效日志,在用例执行结束后,可以通过扫描日志知道当前用例的执行情况,比如执行时间、执行时长、用例名、日志级别等等。执行时间和执行时长通过记录日志的时间点来获取,用例名在记录日志的时候一并写入,日志级别需要根据用例执行过程中有可能出现的情形来区分。游戏运行期间产生的报错,能及时捕捉并放入测试分析中。另外,在拿到测试报告分析失败用例,很多时候单纯依靠文本日志的分析,并不能完全定位原因,事后若能回放执行过程,往往更容易复现当时的测试场景,可以在用例执行期间,定时进行游戏截图或者直接录制游戏视频。游戏截图和视频录制对分析固然有用,但也需要注意在手游中的应用尺度,如果大量产生截图和视频,存储空间很快用尽,事必会影响用例的执行。

编写用例之前,需要实现人工测试中所有可能出现的操作接口,比如单击、双击、输入、拖拽、获取值等等。实现这些操作后,再指定相应的操作对象,就可以完成从人工操作到代码执行的转换了。在实现这类接口时有两种方式,一种是纯用户操作模拟,以单击为例,向屏幕某个点,发送点击操作,后面提到的Airtest Project 为这种方式,可以更真实的模拟用户操作。另一种是从控件出发,让UI 上的某个控件响应某个事件(比如单击),采用这种方式,单击事件一定会成功。比如下图2.4 中,界面有确认框,如果此时想要点击包裹,正常人工操作,必须先点击确认按钮,关闭确认框,再去点击包裹按钮。使用Airtest Project 的操作流程同人工操作一样,而这时如果让包裹的UI 控件直接响应单击事件,一般情况下,无论有无确认框,包裹面板均会打开(当然这里也和程序产品里实现的UI 响应事件有关)。unity 引擎中提供了响应某个事件(单击、输入等等)的接口,可以直接使用,使用之前,需要注意,相应的接口产品代码是否重写过。

图2.4 确认框
执行测试用例期间,记录各项性能数值,比如FPS、内存、网络流量等等,有助于分析版本潜在的性能问题。每个性能数值会有不同种类的计算方法,可以根据项目组的需要来记录,比如内存,可以记录整个游戏运行期间进程的内存变化,再比如unity 引擎会提供一些相关的接口,NativeUtility.GetUsedMemory()、Profiler.GetTotalReservedMemory()等等。在生成用例报告的时候,将这些性能数据一并制成图表,可以与历史数据对比,设置一定的阈值进行预警,协助产品组发现问题。

实现了以上三部分内容,客户端相关框架已经完成,可以开始尝试编写测试用例了。

2.3 第3 方工具airtest 框架搭建

Airtest Project 是网易自研的一套自动化测试框架,这套框架采用图像识别和UI 树结构的技术,可以通过编写少量代码,甚至不用手动编写任何代码,就可以完成一个自动化测试用例,容易上手,学习成本低,编写用例操作也很简单,目前部分项目组采用的是这种方式。

Airtest Project 内部有两个框架:一个是Airtest,这是一套基于图像识别的UI 自动化测试框架,该框架的使用逻辑很简单:在编辑器上选择相应的操作,比如“click 点击”,截取需要点击按钮的图片,就可以实现点击某个按钮的操作。系统会通过图像识别,在当前UI界面中寻找截图所在的位置,然后在屏幕上发送一个click 的操作(注意这里的click 是完全模拟真机的点击)。操作虽然简单,但缺陷也同样存在:比如当前UI 界面中存在两个同样的截图;再比如很难进行非UI 操作或者逻辑验证。另外,图像识别有时候会有误差,Airtest会提供一个可选的精准度,这个值设得太高,容易出现误判,比如画面突然出现某个特效;精准度若设得太低,容易把一些非正确的图像区域筛选进来等等。另一个框架是Poco,基于UI 树结构,类似于unity 里UI 树结构。根据unity 里控件的路径,来指定操作的对象。相比Airtest,这种方式可靠性更高,只要找到了正确的控件,不会出现图像识别不准确的问题。Poco 获取路径很方便,可以一键获取操作的元素。相比在unity 里纯人工找控件会方便许多。Airtest Project 使用和操作起来都很方便,接入和使用官网提供了操作指引,可以参考官方文档

Airtest Project
​airtest.netease.com/

进行提供了一些基础框架,有一定的局限性,比如无法进行逻辑验证,可以根据各个项目组的具体情形进行二次开发。

三、自动化测试用例脚本的编写
有了自动化测试框架可以驱动用例执行,接下来就是测试用例的编写。在本文开头已经提到了,自动化测试用例也分“好”、“坏”,那么什么是好用例,什么是坏用例?测试用例的根本目地是为了判断被测产品是否有问题,是衡量被测产品质量的标尺而存在。因此,它具有一个重要特性:在测试脚本和被测产品都保持不变的情况下,测试结果应是稳定的,不变的。根据这个特性,“坏”用例并不是指测试不通过的用例,更不是测试通过的用例,而是指那些在相同条件下,偶尔通过、偶尔不通过的测试用例。反之,“好”用例则是表现稳定的用例。“坏”用例破坏性大,如果用例本身不具备稳定的结果输出,就无法准确地衡量被测产品是产品问题还是用例本身的问题。如果每次测试结果都不能直接说明问题,需要进一步分析,将直接导致大家对测试用例失去信心。也就是说,项目组内人员会把测试用例的不通过,当成“warning”而非“error”,最终结果就是自动化测试慢慢被抛弃。正如“破窗理论”:当房子上的一扇窗户玻璃被打破,如果不及时修复,将会有破坏者破坏更多的窗户。“坏”用例也是如此,当出现时,如果不抓紧修复,整个测试用例集甚至自动化测试结果的可信度都将快速下滑。对“坏”用例采取零容忍的态度,有助于整体自动化水平和质量的提升。可以通过建立“坏”用例档案,追踪来源,督促负责人员跟进解决。一个“好”用例除了结果足够稳定以外,还需要具备良好的结构设计,良好的可读性、可维护性,这对测试用例编写人员要求较高。输出“好”用例,并不是一朝一夕的事情,是测试人员能力的体现,需要在平时工作中逐渐积累,通过多读、多思考、多写来快速提高自动化用例编写能力。

用例编写的质量会直接影响测试的质量,自动化测试用例的设计可以参考人工测试时设计用例的思想来完成,考虑到不同自动化框架的适用范围,并不是所有的手工用例都要转为自动化用例。下面针对前面提到两种不同框架搭建的形式,分别介绍用例如何编写。

3.1 基于项目组搭建框架的测试脚本

项目组内搭建自动化测试框架,主要使用产品代码来编写用例,这里介绍两种常规方法,一种是完全基于游戏代码来编写自动化测试用例,另一种与伏羲工作室合作,运用人工智能技术训练出一套模型来实现用例的行为。

3.1.1 测试脚本语言的选择

正式编写用例之前,首先需要确定自动化测试用例使用哪种语言,项目组内搭建框架,大都会选择与产品代码一致的语言。以倩女手游产品代码为例,主要开发语言如下:

除了以上几种语言外,Android 插件会使用Java 和C++来编写,IOS 插件会使用objective-c和C++来编写,插件的开发可以忽略。前面提到,测试用例脚本运行在客户端,主要参考客户端语言(C#、Lua)。下面从类型、上手难度、动态性、对产品入侵程度来对比:

C#是一种编译型语言,Lua 是脚本语言,这也决定了C#一般不支持动态更新,而Lua 可以;相对于Lua,C#的上手会难一些;C#主要用来开发客户端引擎以及部分逻辑代码,Lua主要用于上层逻辑的开发,如果测试脚本采用C#,对产品代码的入侵程度也会更高。经过综合分析,最终选择Lua 作为测试用例的开发语言,如果在语言选择上如果有疑问,不妨采用同样的思路试试看。

3.1.2 基于游戏代码的测试脚本

传统编写自动化测试用例大都基于游戏代码来完成,凡是游戏代码能支持的功能和相关检查,测试用例里也可以做到,这种方式很多时候需要借助产品代码来实现测试点的检查,相对来说,对代码的阅读和编写会有一定要求。这种方式并不是万能的,实现主体是产品代码,对于特效、动作等资源表现是否正常,代码很难去判断,需要结合人工测试或者图像识别的方式来完成。比如福利界面,如下图3.1,领取双倍经验时,该选项处会出现一个红点,单纯从代码上不太容易在UI 层判断红点的存在。

图3.1 领取奖励界面
如何编写自动化测试用例,这里举一个简单的例子:判断包裹内是否有物品。在人工测试中经常需要知道,此时包裹里是否有物品。正常人工操作流程:1.游戏面板找到包裹按钮并点击(如图3.2),PC 端也可以使用快捷键(如果有的话)方式直接打开;2.待包裹面板打开后,查看包裹内是否有物品(如下图3.3)。

图3.2 游戏主界面

图3.3 包裹界面
上述操作过程,将其一步步分解,流程图如下3.4。整体流程符合从一个状态转移到另一个状态,也就是状态机,状态机的思想广泛应用于硬件控制电路设计中,它也是软件上常用的一种方法。状态机可以归纳为4 个要素:现态、条件、动作、次态。

图3.4 流程图
现态:当前所处的状态;

条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移;

动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态;

次态:条件满足后要迁往的新状态。

上述流程图也可以用状态迁移表来表示,如下表3.1 流程表,在流程表中,会将文字描述替换成相应的字符,以便后面编写伪代码。

表3.1 流程表

图3.5 伪代码
将上述流程图,采用Lua 代码写出来,如图3.5,首先会定义所有的状态,将初始状态设为Start,接下来,根据状态迁移表,满足相应的条件,执行相应动作,并进行状态迁移。

细心的同学大概已经发现,每个状态结束后会有return(L16、25、35),从状态里return出来,也就意味着,一个状态里的操作执行结束后,后面的代码不会被执行。这里是BUG吗?为了能执行到后面的操作,是否可以把return 去掉,如果去掉return,这段代码会如何运行,如下图3.6。

图3.6 伪代码
如果包裹按钮存在(L9),点击包裹按钮(L10),将状态置为status1(L11)。接下来,L18 中if 成立,进入第二个大状态,condition3(L19)是否为true 呢?说明:游戏客户端的很多操作异步进行,比如点击包裹,到包裹面板打开为异步的过程。意味着,在包裹面板打开之前,状态有可能已经被置成status1,从而进入到下一个状态(L18),此时包裹面板未打开,进入else(L22),status 被置成start,接下来状态并不是status2(L27),这段代码执行结束。由此看来,将return 去掉,这段代码和预期执行流程还是有出入。我们预期流程是,如果状态为start,能返回至这段代码入口处(L8),根据当前不同的状态及条件,决定下一步的操作。如何反复执行这段代码呢,采用for 循环不断遍历是否可行?测试用例代码运行在客户端,一般情况下,游戏的逻辑为单线程,采用for 循环,会将游戏主逻辑挂起,从而导致客户端卡死。事实上,为了return 出来能再次进入这段代码的入口处,可以通过每隔一段时间去调一次这段代码来实现多次进入入口的目地。

每隔一段时间执行一次,会用到游戏里经常使用的定时器功能,定时器是用来控制在未来某个时间点调度执行某个函数的一种机制。以倩女项目组为例,定时器是通过模仿linux内核定时器原理来实现的。倩女这边定时器相关的函数有两个:

RegisterTickWithDuration(TickFun, Interval, Duration, …)

表示每隔Interval 的时间去执行一次TickFun 函数,这个过程会持续Duration。

RegisterTick(TickFun, Interval, … )

表示每隔Interval 执行一次TickFun 函数,与它配合使用的是UnRegisterTick(tick),用来注销定时器。

使用这类函数时需注意,间隔时间Interval 存在最小值,倩女这边Interval≥10ms。间隔时间就像一把尺子,存在最小刻度,Interval 的最小值便是游戏时间度量的最小刻度,设置间隔时间时,Interval 必须为最小值的整数倍。

有了定时器后,回头看看图3.5 中的伪代码,完整流程伪代码如下图3.7,将所有状态的执行放到局部函数run()中,注册定时器函数(L28),每隔1s 执行一次run 函数(L28),在超时(L24)或者所有状态结束后(L20),会注销定时器。

图3.7 伪代码(定时器)
到此为止,查看包裹内是否有物品测试场景的用例代码就完成了。游戏中无论多复杂的玩法和系统,最终都可以像这样一步步拆分,变成一个个状态机来执行。比如日常任务、主线任务、副本、各项基本功能和系统等等。下面以主线任务为例,来介绍具体的游戏功能该如何去设计用例。

图3.8 主线任务流程图
在编写主线用例之前,不要急于动手书写代码,按照前面提到的状态机思想,先理清思路:主线任务从用例类型上属于一系列子任务的集合体,由策划表来进行数据驱动;从任务类型上,主线任务涉及到的任务类型虽然多,也是有限的,比如与NPC 对话、找物品、使用物品、打怪、购买物品、副本、几个子任务的组合等等。在理清任务类型后,再对每个子任务以状态机的形式进行步骤分解(条件、动作、状态、次态),最后再整合所有子任务代码。图3.8 为主线任务流程图,图中只列举了购买物品这一个分支的流程。流程图看起来比较复杂,却可以帮助理清思路,让我们在编写用例过程中少走弯路。刚开始编写自动化测试用例的同学,在遇到复杂用例时,可以尝试先画流程图,有经验的同学可以省去这一步。经过梳理后,对整个主线任务会有直观的了解,再动手写代码。

实际编写测试用例时,可能还会遇到各类问题,可以多向组里有经验的同学请教,提高用例质量。有的同学用例代码可读性高,维护成本也低,有的同学用例代码,过段时间可能连自己也很难读懂,维护成本也高。为了避免出现这类情形,在用例编写时,尽可能做到以下几点:

  1. 一个状态尽可能只做一个操作:在一个状态里涉及到的操作越多,意味着分支越多,一方面可读性低,另一方面这样的代码聚合性太低,不符合代码编写的基本思想,后期维护成本也高。

  2. 没有检测的代码没有意义:新人很容易误入这个雷区,在一个操作后,下一个状态一定要检查该操作的执行是否正确,比如点击包裹按钮后,需要检查包裹面板是否打开,而不是直接开始下一步操作。自动化测试的整个过程中无人执守,每个操作的结果是否正常,需要代码来检验,没有检测的自动化测试用例没有任何意义。

  3. 持续维护完善测试用例:产品代码一直处于不断迭代中,相应的测试用例也需要及时更新,让测试用例不断完善,并且适应更加恶劣的测试环境。

3.1.3 基于人工智能驱动的测试脚本

随着人工智能的普及,人工智能技术在游戏里有了越来越多的应用,强化学习这一套技术在测试工作中也能得以运用。通过AI 训练的方式,对玩家周围信息和当前任务进行分析,模拟真实玩家操作客户端的方式,来完成任务。人工智能的方式需要与伏羲工作室合作,通过不断与环境交互,利用环境给出的奖惩来不断改进玩家的行为策略,找出一条最优路径。在平时的测试中,以主线任务为例,任务多,人工测试耗时长,且主线任务类型是有限的,比如寻路、对话、打怪、采集、使用物品等等,回归测试时,所有主线任务的回归费时又费力,通常在人工回归时,会选择一部分重点主线,而没覆盖到的部分总存在一定风险。对于这类测试,可以选择传统脚本的方式来回归(3.1.2)。传统方式,需要对整个主线任务所涉及到的相关流程及结构熟悉后,并进一步了解实现方式及原理,再进行归纳总结,抽象概括,理出一套通用流程,才能开始代码的编写。有的项目组开发人员,在主线的任务设计上,前后可能使用了不同的设计方案,并且是共存的,需要额外处理,兼容不同的方案,这对用例编写的同学来说,无疑也是一种挑战。此时借助AI 技术,可以大大减少代码编写量。采用AI 技术编写用例,流程如下:需要向AI 服务器发送当前客户端的状态信息(比如UI 上的显示、附近NPC/Monster/Item、任务信息等等),AI 服务器通过一系统算法计算后,向游戏客户端返回当前客户端玩家下一步要执行的操作,比如跑动到某个目标位置附近、与NPC 对话等等。向AI 服务器发送状态信息,可以从游戏服务器向AI 服务器发送,也可以从游戏客户端直接向AI 服务器发送,分别如图3.9、图3.10,代表产品方与AI 服务器两种不同的连接方式:

图3.9 游戏服务器直连
在上图3.9 中,游戏服务器与AI 服务器直连,游戏客户端会将自身的状态信息发送给游戏服务器,游戏服务器再将这些状态信息发送给AI 服务器,AI 服务器经过强化学习算法,根据当前的状态,计算下一步动作并返回给游戏服务器,游戏服务器拿到数据后,再分发给对应的客户端执行。游戏客户端发送请求、接收操作指令,对于客户端而言,无需感知AI服务器的存在,客户端可以保持本身与游戏服务器之间的通信方式不做任何修改。另外,一些客户端上无法拿到的状态信息,可以在向游戏服务器发送请求的时候,缺失的数据在请求链路中,由游戏服务器从中整合补全后一并发送给AI 端,客户端无需重复获取这类信息。缺点是,游戏服务器需要处理来自不同客户端的状态信息,并将收到的动作消息,返回给相应的客户端。当客户端自身状态信息过于复杂,需要发送的信息数据较多时(比如返回视野范围内所有怪物信息),受限于游戏本身设计的网络传输数据大小上限,有时难以一次性将所有信息发送给服务器端。

图3.10 客户端直连
图3.10 中,游戏客户端与AI 服务器直连,由游戏客户端将自身状态信息发送给AI 服务器,AI 服务器经过一系列算法计算后,向客户端返回下一步的动作,这种方式减少了数据转发的步骤,需要发送的信息数据较多时,不再受限于游戏本身设计的数据上限。缺点也同样存在,遇到一些客户端上无法拿到的状态信息,需要提前向游戏服务器请求,客户端拿到后,再一并发给AI 服务器。因此,如果大部分客户端状态信息都可以在客户端获取到,使用客户端直连AI 服务器的方式会更高效。

以上两种方式各有利弊,可以根据各自项目组特点来决定。产品游戏和AI 服务器之间的通信方式,可以采用长连接,也可以采用短连接。采用长连接建立通信,无需感知双方调用的具体逻辑,需要产品方和AI 方沟通好一套网络通信链路。采用短连接,比如http 请求,实现比较方便,产品有现成的http 模块可以直接通信,但每次消息传输都需要重新建立连接。

采用AI 的方式编写用例,最合适的应用场景莫过于任务类的测试用例,这类用例在执行过程中,流程如图3.11,在用例开始之前需要做一些准备工作,比如清除现有任务、添加物品、传送等等,接下来只要任务未完成,会不断将状态信息发送给AI 服务器,并等待相应的返回操作。在发送状态时,也涵盖了超时判断逻辑,避免一直在某个步骤循环导致整个脚本无法正常执行。

图3.11 测试用例流程逻辑
超时处理逻辑如图3.12:重复执行N 个动作之后,当前任务还未完成,则认为该任务超时,会直接采用指令跳过该任务,继续下一个任务的流程。

图3.12 超时处理逻辑
采用强化学习的方式,新任务需要训练从而得到模型,正常情况下,游戏产品方会有两套环境,一套是回归测试流程,一套是新任务训练流程,如图3.13 所示。如果有新任务加入,无需修改脚本,AI 算法可以训练生成新的模型。训练时,采用多客户端可以节省时间,比如1-150 级主线任务,耗时10h 训练完成,如果用5 个客户端同时进行,只需2h 就够了。

图3.13 游戏方测试环境流程
两套环境各不互相感知,也不会相互影响。回归环境,负责版本做好后对任务进行回归,此时算法使用已有的模型,尽可能以最优路径快速回归所有任务。训练环境负责在新任务接入时,先进行尝试训练,并查找最优路径,训练完成后,将得到的模型和最优路径更新到回归服务中,用于后续的回归任务,更新过程游戏方不感知。

目前,伏羲工作室已经训练出一套完整主线流程方案,其它类型仍在不断扩充中,比如PVP、PVE 副本等等。总结3.1.2 与3.1.3 两种不同的用例书写方式,如下表3.4:

表3.4 对比不同的用例书写方式
基于游戏代码的传统编写用例方式,需要用例编写人员对相应模块流程、实现有一定了解,借助产品代码的实现逻辑编写用例,而基于强化学习的主体在于不断向AI 服务器发送状态信息,并接收AI 服务器的动作信息进行相应操作。相对而言,传统方式的上手难度要高于强化学习。同一个用例,基于游戏代码的方式用例的代码量比较多,而基于强化学习的用例代码量相对会少一些,特别是对于一些复杂流程,比如像主线任务,越复杂的任务,代码量的对比会更明显。从使用范围来看,基于游戏代码的方式适用范围较广,而基于强化学习的方式必须借助AI 训练出来的模型,如果这类系统从未训练过,是无法编写测试用例的,比如队伍相关的模型未训练过,队伍的用例就无法采用强化学习的方式来编写用例,目前伏羲工作室已经有了主线任务、日常任务、PVP、PVE 等相关模型,其它方面还没有涉及。因此就目前而言,传统编写用例方式使用范围更广一些。鉴于上述特点,若采用项目组内开发的框架,存在AI 训练模型,可以优先考虑强化学习的方式,否则可以使用传统编写用例的方式。

3.2 基于第3 方工具airtest 的测试脚本

前面已经提到Airtest Project 底层有两种实现方式,不同的实现方式,用例脚本的编写略有区别。借助官网引导,可以迅速上手,官网地址:

Airtest Project
​airtest.netease.com/

Airtest Project的上手相对简单,这里就不展开介绍了,同时也支持定制需求,需要根据具体项目组的需求来决定。

四、手游自动化测试的特殊性
近些年,伴随手机硬件和互联网科技的发展,游戏产业市场,从PC 端大幅向移动游戏倾斜。随着手游的兴起,市面上涌现了许多针对手游的测试工具,比如前面提到的Airtest Project。区别于传统的端游,手游有着多样化的渠道,大量的手机型号、操作系统、系统版本等等,这无疑也加重了测试的工作量。

4.1 测试平台的多样性

目前市面上,手机系统主要包括IOS、Android,很多手游为了进一步适应市场需求,纷纷推出了电脑版。目前市面上常见的手机型号有上百种,手机不同的设计款式,比如留海屏、折叠屏等等,在游戏测试中也会带来各类兼容性问题。为了适应如此庞大的市场分类及测试需求,部门里也产生了相应的测试团队,比如雷火这边的MTL 实验室,一些常见的、通用问题可以移交到该实验室,可以减轻项目组内的测试工作,MTL 实验室所涉及到的测试工作,参考MTL 网站。前面提到的自动化测试用例,MTL 实验室有相应的平台及测试人员,可以使用MTL 实验室的各类手机来执行,协助项目组发现更多问题。

4.2 性能优化

随着手游的普及,玩家对游戏画面呈现要求也越来越高,而越精细的表现,也会带来更多的性能问题,比如手机发热、流量过大、网络延迟等等。但往往受限于手机自身的性能瓶颈,开发组总是想尽办法,在手机性能瓶颈范围内,尽可能提升玩家的用户体验。自动化测试用例的作用是为了验证系统相应功能,如果测试脚本本身性能消耗较大,特别是手机,其开销也是不能忽视的。通常情况下,在运行自动化测试用例时,会记下当前客户端的各项性能数据,比如FPS、内存、流量等等,如果测试脚本本身的性能开销较大,这些性能数值也会存在一定误差。

图4.1 UI 树结构
以项目组内搭建测试框架为例,点击包裹按钮,如图4.1,倩女手游起初的做法是在unity的UI 树结构中,从根结点开始,递归查找名为“PackageButton”的控件,再让该控件响应“onClick”事件,采用这种方式确实能找到“PackageButton”控件,但效率极其低下,在手机端运行测试用例,会严重发热,电量很快耗尽。实际上,从树的根结点到PackageButton路径是确定的,可以直接通过根结点的相对路径定位到相应结点,避免递归查找的过程。这个看似小的改动,却能大大提升效率,让测试脚本的开销基本可以忽略不计。以上只是脚本性能问题其中一个例子,在实际应用中,可能还会出现其它问题,这时大家多思考,应用所学软件知识,分析原因,定能解决它们。

五、自动化框架的其它应用
自动化测试框架除了可以用来验证游戏产品的功能外,也可以在其它方面有所应用。比如下文提到的版本检查、性能测试等。

5.1 版本检查

除了前面提到的游戏产品功能以外,版本检查也是很重要的一个方面。比如针对每个版本的策划表扫描,现在大多数项目组所做的策划表扫描是针对策划所使用的表格来进行的,比如excel 表、json 表等等。一般来说,游戏不会直接去读取策划所使用的表格,这其中涉及到转表及读取的过程,转表会将策划表转化成游戏可读取的形式,也就是说,策划所使用的表和最终游戏里的内存数据还是有差异的。从策划表到内存数据,中间涉及了其它步骤,所以倩女手游这边为了保险起见,除了会去检查策划表是否正确以外,还会去检查读入内存后的数据是否正确。检查内存数据,需要启动游戏,这时可以考虑将相关规则写成自动化测试用例的形式,每日检测。不过,截止到目前为止,还没有发现策划表正确,内存数据不对的情形,这从另一方面也说明项目组转表、读表的过程很稳定。

另外,比如闪退、报错检查,都可以在用例运行期间来捕捉。还有一些特定操作,比如快速多次点击某个装备放入仓库中,查看是否放入成功或者是否会复制多份等等,这类特殊操作人工有时候不一定能复现出来,用脚本往往能事半功倍。还有一些特殊的检查,比如像位置障碍点的检查,举个例子,策划在地图上刷NPC 或者填NPC 位置的时候,填了一个寻路不可达的点,人工去回归每个NPC,工作量可想而知,这时候不妨也考虑采用自动化测试的方式来回归。

5.2 性能测试

前面已经提到,用例运行期间可以记录客户端的各项性能数值,用以性能分析。实际上,根据项目组的需求,也可以设计特定的用例,用以衡量服务器的性能。

比如某国战项目组在技能开发期间,对于技能的设定不仅需要考虑技能本身的需求,国战游戏对服务器性能要求较高,技能计算太过复杂会直接影响到单进程上群战玩家人数上限,过于简单又会影响到PK 的趣味性。技能频繁迭代期间,每改一版,策划都希望知道这版技能的服务器性能如何。对于MMORPG 这类游戏,在不考虑等级的情况下,一个职业技能有几十上百个,所有职业能有上千个技能,人工测试工作量庞大,这时候不防考虑采用脚本的方式来进行。

例如图5.1 中,登陆1000 个玩家,同时释放某个职业技能,记录所有技能释放耗时,拿到成功释放次数后,可以计算得出平均释放一次该职业技能的服务器耗时,以此获取所有技能测试数据,如下表5.1,这份数据可以在一定程度上反映整体技能的设计在性能表现上是否合理。

这里只列举了技能释放耗时这一项测试指标。测试的工作服务于产品质量,具体需要测试哪些数据可以根据各自项目组具体情况来决定。

图5.1 1000 个玩家同时释放职业技能

表5.1 技能释放结果
5.3 其它测试

自动化测试框架除了可以进行版本检查和性能测试外,只要善于发现,平时工作中很多人工测试都可以用代码来代替,比如版本更新检查、图像智能测试、外网新服测试等等。

版本更新检查:倩女手游这边曾经出现过版本更新问题,因版本升级,对于某个很老的版本不再提供版本更新功能,需要重新下包才能正常进入游戏。在功能外放前,项目组内人员一致认为外网不太可能会有这么老的版本,QA 在测试的时候也忽略了这一点,外放后,真的有外网玩家进不了游戏,而且使用了很早的老版本。为了避免同样问题再次发生,在发布新版本之前,会逐个对不同的老版本进行版本更新检查。当然,这个过程都是代码自动执行的。

图像智能测试:随着和伏羲工作室的合作越来越多,有些人工测试容易漏掉的工作,可以采用人工智能测试作为补充。比如游戏内资源缺失、大面积同色色块的问题等等。外网新服测试:倩女手游这边,在开新服之前,会人工跑一轮简单的测试用例,保证基本功能正常。自从有了自动化测试框架后,这部分工作从人工变成了自动化,节省了组内人工测试的工作量。

六、总结

自动化测试的目地归根结底是为了提高测试效率,更好地服务于产品质量。工欲善其事,必先利其器,随着游戏系统规模日益扩大,以及应用领域的不断拓展,对系统的测试也变得更加困难和复杂,传统的人工测试的局限性也越来越明显,搭建一个高效的自动化测试流程尤其重要。自动化软件测试技术可以克服传统测试技术的许多问题,它依据的是一套严密的测试法则和评估标准,具有完整的自动测试过程,可以避免测试人员惯性思维所导致的测试疏漏,也可以减少由于手工测试中繁复的重复工作所导致的人为差错。尽管长期来看(尤其是针对回归问题的)自动化测试,可以带来开支上的节省,但将所有测试短期内全部自动化有可能产生巨大的开销,测试“自动化”了,但测试结果分析、测试脚本维护和编写仍然需要人力投入。在实际工作中,自动化做到何种程度,仍需具体情况具体考虑。

编辑于 2021-10-22 12:18

游戏自动化测试好文分享相关推荐

  1. Facebook 游戏开发更新文档 API 参考文档 v6.0

    Facebook 游戏开发更新文档 API 参考文档 v6.0 更新日志 1.排行榜 此版本全新推出排行榜 API!提供一套强大的 API, 使得游戏可获取排行榜.查询得分 情况和设置新分数(支持分数 ...

  2. 游戏美术设计干货分享:制造“冲突”,提高画面张力

    游戏美术设计干货分享:制造"冲突",提高画面张力 http://www.gameres.com/475470.html 发布者: 小篱 | 发布时间: 2015-12-9 14:1 ...

  3. 【Game Of AutoTest】4、游戏自动化测试的稳定性保障

    在游戏自动化测试技术落地过程当中,如何保证自动化测试的稳定性,是一个需要重点优先解决的困难问题. 以手机游戏客户端自动化测试为例,和一般服务架构的自动化单元测试或集成测试不同,自动化驱动的过程,其本质 ...

  4. 借游戏+IP网文双核驱动,触宝走上增长的正确轨道

    在互联网行业,纵观阿里.腾讯.美团.小米等成熟的生态企业,他们往往具有更好的业务延展性,不断突破自身边界,持续拉升其天花板. 除了巨头之外,不少中小玩家也在积极拓展新的点与面.其中,触宝借场景化内容应 ...

  5. android新浪登录接口,新浪游戏AndroidSDK接入文档—服务端.md

    # SNG联运游戏平台接口文档ForCP(服务端) ## 1.用户接口 ### 1.1.用户信息校验接口(服务端) http://m.game.weibo.cn/api/sdk/user/check. ...

  6. Android调用系统分享和指定app分享-微信朋友圈图文分享和qq分享

    Android调用系统分享和指定app分享-微信朋友圈图文分享和qq分享 标签: Android系统分享QQ分享朋友圈图文分享 2016-09-27 22:54 279人阅读 评论(0) 收藏 举报 ...

  7. Unity游戏帧同步技术分享篇【01】帧同步解决方案概述

    前言: 1.0 帧同步原理与简介 A.什么是帧同步? 帧同步是一种前后端数据同步的方式,一般应用于对实时性要求很高的网络游戏. 其基本实现流程及思路可以概括为: 1.所有客户端每帧上传操作指令集到服务 ...

  8. 红米k40游戏加速开启方法分享

    我们经常在手机上玩游戏,有的时候感觉玩游戏有点卡,其实红米手机上带有有游戏加速功能的,可以开启游戏加速功能来提升我们玩游戏的流畅度.那红米k40的游戏加速在哪里设置呢?换换为大家整理出了详细的开启方法 ...

  9. 3D游戏建模学习就业会困难吗?10年资深游戏美术大拿分享就职面试心得

    一.入行,我该找大公司还是小公司? 比较推荐推荐去大公司.虽然现在很多中型公司甚至小公司给新手的福利待遇和薪资反而比大公司要高.但如果有机会,还是推荐大公司. 因为在小公司,干多少年都是那个样子,接触 ...

最新文章

  1. 人与人的差距在于认知
  2. Android 开关按钮切换,类似于iphone 效果,view实现
  3. SQL Server里的INTERSECT
  4. java实验金额转换_java 数字金额转换中文金额
  5. vs c语言程序调试方法,VS2015中的常用调试技巧分享
  6. UI实用素材模板|app底部导航栏的图标可临摹素材,教你分析!
  7. CMakeLists编译
  8. 苹果鼠标滚轮驱动_双飞燕血手幽灵V8M Max电竞鼠标兼具功能和性价比
  9. 统计学的Python实现-013:频度分布表
  10. 中国版Kindle Paperwhite使用评测
  11. mt4怎么用云服务器跟单,免费好用的跟单系统 神速MT4跟单ea系统使用教程
  12. Android烧录镜像文件介绍
  13. Vulkan编程指南翻译 第六章 着色器和管线 第2节 SPIR-V 概述
  14. 编译A-LOAM,catkin_make后PCL报错
  15. php 图片印章_给图片加字,印章在线生成
  16. 8个让程序员追悔莫及的职业建议
  17. 计算机毕业设计(附源码)python众筹平台
  18. 【Docker】9、Docker-Compose安装轻量级分布式日志服务Graylog
  19. Ubuntu中Kdevelop的安装和使用
  20. 采药(洛谷P1048)

热门文章

  1. signature=71c2363ad5776ff530a286dd0cdf792c,SUSY Multilepton Signatures at Tevatron
  2. 思维导图帮助记忆,个人评估和辅助决策
  3. 02 Oracle 11gR2服务端(Server)安装教程
  4. VMWare虚拟机找不到eht0解决办法
  5. linux下conda使用教程
  6. NLP自然语言处理学习笔记(八)(转自咕泡AI)
  7. 丰田生产方式的自働化与负反馈分析
  8. 体验windows server2008服务器操作系统
  9. mongo基础之 CRUD操作(3)
  10. vue使用echarts绘制中国地图