51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2942|回复: 3
打印 上一主题 下一主题

[转贴] Python 与游戏测试 (小工具篇)

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2017-6-27 13:13:42 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
最近在TesterHome游戏测试群里,有时候会看到有童鞋问,游戏测试人员学了Python,可以干点什么。
很多童鞋初学Python,学习了语法和基础类库后,开始迷茫如何实际使用到工作中去,其实Python可以做的事情是很多的,将日常工作的一些事情自动化,对我们的工作效率有很大的提升。
本文面向Py新手,分享一些辅助工作的小工具思路。以下例子都是在Win10 + Py3.5下完成。
调用CMDsubprocess是Python自带的子进程管理模块,定义有数个创建子进程的函数,也提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。
简单理解就是,你通过CMD敲的命令,都基本可以用subprocess来实现批量处理。
例子1:批量SVN操作
以更新SVN为例,这是一个频繁的操作,尤其是多个SVN目录需要一一更新的时候,手动起来是挺麻烦的。
  1. import subprocess
  2. subprocess.Popen(r'TortoiseProc.exe /command:update /path:"C:\project\策划文档" /closeonend:0')
  3. subprocess.Popen(r'TortoiseProc.exe /command:update /path:"C:\project\配置文档" /closeonend:0')
复制代码
例子2:adb命令的封装
做安卓手游测试的时候,adb是常用工具,我们可以通过它,进行apk的安装,卸载,截图,获取APK信息,性能数据,获取手机信息等等操作。
比如获取当前运行在前台的apk的package和activity名称

  1. def run_cmd(cmd):
  2.     """执行CMD命令"""
  3.     p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
  4.     return [i.decode() for i in p.communicate()[0].splitlines()]


  5. def get_apk_info():
  6.     """获取apk的package,activity名称

  7.     :return: list  eg ['com.android.calendar', 'com.meizu.flyme.calendar.AllInOneActivity']
  8.     """
  9.     result = run_cmd("adb shell dumpsys activity top")
  10.     for line in result:
  11.         if line.strip().startswith('ACTIVITY'):
  12.             return line.split()[1].split('/')

  13. print(get_apk_info())

  14. output: ['com.android.calendar', 'com.meizu.flyme.calendar.AllInOneActivity']

  15. 比如查看当前apk的内存占用

  16. def get_mem_using(package_name=None):
  17.     """查看apk的内存占用

  18.     :param package_name:
  19.     :return: 单位KB
  20.     """
  21.     if not package_name:
  22.         package_name = get_apk_info()[0]
  23.     result = run_cmd("adb shell dumpsys meminfo {}".format(package_name))
  24.     info = re.search('TOTAL\W+\d+', str(result)).group()
  25.     mem = ''
  26.     try:
  27.         mem = info.split()
  28.     except Exception as e:
  29.         print(info)
  30.         print(e)
  31.     return mem[-1]

  32. output: 37769
复制代码
比如备份当前apk到桌面
  1. def backup_current_apk(path=r"C:\Users\jianbing\Desktop\apks"):
  2.     package = get_apk_info()[0]
  3.     result = run_cmd("adb shell pm path {}".format(package))
  4.     cmd = "adb pull {} {}".format(result[0].split(":")[-1], os.path.join(path, "{}.apk".format(package)))
  5.     print(cmd)
  6.     run_cmd(cmd)
复制代码
再进一步,将常用的adb操作封装为一个ADB工具类。社区里也有童鞋之前分享过,传送门。
处理文本例子:在整个文件夹中搜索关键字
某天策划说,这个版本他删掉了某个道具,让我检查下有没有删漏的地方,这个道具产出的地方不少,最佳的检查方式是各个相关配置表看下还有没有配置这个道具。
那就写个脚本遍历整个文件夹来搜索指定关键字吧。
  1. import os


  2. def get_files_by_suffix(path, suffixes=("txt", "xml"), traverse=True):
  3.     """从path路径下,找出全部指定后缀名的文件

  4.     :param path: 根目录
  5.     :param suffixes: 指定查找的文件后缀名
  6.     :param traverse: 如果为False,只遍历一层目录
  7.     :return:
  8.     """
  9.     file_list = []
  10.     for root, dirs, files in os.walk(path):
  11.         for file in files:
  12.             file_suffix = os.path.splitext(file)[1][1:].lower()   # 后缀名
  13.             if file_suffix in suffixes:
  14.                 file_list.append(os.path.join(root, file))
  15.         if not traverse:
  16.             return file_list

  17.     return file_list


  18. if __name__ == '__main__':

  19.     keyword = "XXX宝箱"

  20.     files = get_files_by_suffix(r"C:\project\config")

  21.     for file in files:

  22.         with open(file, 'r', encoding='utf-8', errors='ignore') as f:
  23.             content = f.read().lower()
  24.             position = content.find(keyword.lower())

  25.             if position != -1:
  26.                 print("Find in {0}".format(file))
  27.                 start = position - 100 if position - 100 > 0 else 0
  28.                 end = position + 100 if position + 100 < len(content) else len(content)
  29.                 print(content[start:end])
  30.                 print("_" * 100)
复制代码
操作远程服务器例子1:查看内网发版时间
有时候问开发,最近一次内网服务端发版是什么时候?开发回答:有点忘记了。。那就得自力更生了~
手动方式:使用FTP软件连入内网服务器,查看文件的更新日期,从而知道发版时间。
懒人方式:Py大法好~
paramiko是Python很有名的第三方库,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接。
  1. import paramiko
  2. import time

  3. _transport = paramiko.Transport("192.168.1.10:22")
  4. _transport.connect(username="root", password="XXXXXX")
  5. sftp = paramiko.SFTPClient.from_transport(_transport)

  6. result = sftp.listdir_attr("/data/www/sg/sg_dev/socket/conf/config/treasure")
  7. print("发版时间是:{}".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(result[0].st_mtime))))

  8. sftp.close()
复制代码
例子2:查看内网报错信息
在进行测试的时候,需要多留意服务端是否有新的报错信息,有些报错在客户端并没有什么表现,比如数据进库失败,手动方式:通过SecureCRT连入内网服务器,CD到Log目录下,然后tail -n 200 sg_error.log 查看最新的报错信息。
于是萌生了写一个小工具来定时检测,发现报错信息就保存起来的想法。
  1. import datetime
  2. import paramiko
  3. import time
  4. import os


  5. class ScanError(object):

  6.     def __init__(self):

  7.         self._ssh = paramiko.SSHClient()
  8.         self.last_error_log = None
  9.         self._init()

  10.     def _init(self):
  11.         os.chdir("data")   # 打算将报错信息保存到data目录下
  12.         self._ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  13.         self._ssh.connect("192.168.1.10", username="root", password="XXXXXX")

  14.         error_log = self.get_error_log(500)
  15.         self.last_error_log = error_log

  16.         # 检测最近三天有没有报错信息
  17.         today = datetime.date.today()
  18.         yesterday = today - datetime.timedelta(days=1)
  19.         the_day_before_yesterday = today - datetime.timedelta(days=2)

  20.         error_log_str = "\n".join(error_log)
  21.         if error_log_str.find(str(today)) > -1 or error_log_str.find(str(yesterday)) > -1 or error_log_str.find(str(the_day_before_yesterday)) > -1:
  22.             self.save_error_log("error.txt", error_log)
  23.             print('内网最近三天有错误信息,请查看')
  24.             os.popen('error.txt')

  25.     def get_error_log(self, num=200):
  26.         cmd = 'cd /data/www/sg/sg_dev/socket/log&&tail -n {} sg_error.log'.format(num)
  27.         stdin, stdout, stderr = self._ssh.exec_command(cmd)
  28.         error_log = [i.decode("utf-8") for i in stdout.read().splitlines() if i]
  29.         return error_log

  30.     @staticmethod
  31.     def save_error_log(file_name, log: list):
  32.         with open(file_name, 'w', encoding='utf-8') as f:
  33.             f.write("\n".join(log))

  34.     def run_forever(self, interval=30, show_error=True):
  35.         """运行检测工具

  36.         :param interval: 检测间隔
  37.         :param show_error: 是否检测到报错就自动弹出显示
  38.         :return:
  39.         """
  40.         while 1:
  41.             time.sleep(interval)
  42.             error_log = self.get_error_log()

  43.             if error_log != self.last_error_log and "\n".join(set(error_log) - set(self.last_error_log)).find("ERROR") > -1:

  44.                 self.last_error_log = error_log
  45.                 file_name = time.strftime("%Y-%m-%d-%H-%M-%S.txt", time.localtime(time.time()))
  46.                 print('{} 检测到内网有新的错误信息'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))))
  47.                 self.save_error_log(file_name, error_log)

  48.                 if show_error:
  49.                     os.popen(file_name)


  50. if __name__ == '__main__':
  51.     ScanError().run_forever()
复制代码
数据库操作例子:找号功能
内网的服务器建了N多的号,有时候看着排行榜某个帐号,想登录看下数据,可以使用Python写一个连接数据库的找号脚本。
  1. import cymysql

  2. player_name = "XXXXXX"
  3. conn = cymysql.connect(host='XXXXXX', user='sg', passwd='XXXXXX', db="dev", charset='utf8')
  4. cur = conn.cursor()

  5. sql = "select * from Player where name like '%{0}%'".format(player_name)   # 模糊搜索,从玩家名称搜索玩家ID
  6. cur.execute(sql)
  7. for r in cur.fetchall():
  8.     sql = "select * from Account where uid = '{0}'".format(r[0])   # 从玩家ID搜索玩家帐号
  9.     cur.execute(sql)
  10.     for row in cur.fetchall():
  11.         print('{0}, {1}, {2}'.format(r[0], r[1], row[2]))  # 打印相关信息

  12. conn.close()
复制代码
扩展开发提供的工具在之前某个项目,开发做了一个给游戏帐号发道具的网页,提供测试使用,操作流程是这样的,在网页上的表单里边,填写玩家的ID,在下拉列表选中要发送的道具(支持模糊搜索),填写数量。
这个网页使用起来,工作效率不高的地方就是,每次添加道具,都需要重新选择道具和填写数量,且添加过程没有记录下来,无法复用。
优化方案,在网页上点击添加道具,其实就是网页给游戏服务器发送了一个HTTP请求,那就直接让Python来代劳吧~
  1. import requests

  2. player_id = 1100000103
  3. server_ip = "192.168.1.21:5000"

  4. data = {"stuffList": []}
  5. url = "http://{}/api/{}/stuff".format(server_ip, player_id)

  6. data["stuffList"].append({"itemID": 1104000007, "number": 1000})  # itemID为1104000007的物品,数量1000
  7. data["stuffList"].append({"itemID": 1104000008, "number": 1000})
  8. data["stuffList"].append({"itemID": 1104000009, "number": 1000})
  9. data["stuffList"].append({"itemID": 1104000010, "number": 1000})
  10. data["stuffList"].append({"itemID": 1104000011, "number": 1000})
  11. data["stuffList"].append({"itemID": 1104000012, "number": 1000})

  12. requests.post(url, json=data, timeout=5)    # 添加道具
  13. # requests.delete(url, json=data, timeout=5)   # 删除道具
复制代码
有没有发现,每个脚本都很简短~

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

  • TA的每日心情
    开心
    2018-2-26 10:26
  • 签到天数: 2 天

    连续签到: 1 天

    [LV.1]测试小兵

    4#
    发表于 2018-2-26 11:18:49 | 只看该作者
    我想了解一下, :return: list  eg ['com.android.calendar', 'com.meizu.flyme.calendar.AllInOneActivity'] 这一句中,[]里面的怎么的参数是怎么获取的呀?
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    3#
     楼主| 发表于 2017-6-27 13:35:29 | 只看该作者
    巴黎的灯光下 发表于 2017-6-27 13:34
    不错的分享,有不少指导意义

    慢慢吸收吧
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    2#
    发表于 2017-6-27 13:34:27 | 只看该作者
    不错的分享,有不少指导意义
    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-10 21:01 , Processed in 0.069898 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表