Python 與遊戲測試 (小工具篇)

最近在TesterHome遊戲測試羣裏,有時候會看到有童鞋問,遊戲測試人員學了Python,可以乾點什麼。

(轉自testerhome社區  煎餅同學的  社區遊戲測試qq羣 581529846 )


很多童鞋初學Python,學習了語法和基礎類庫後,開始迷茫如何實際使用到工作中去,其實Python可以做的事情是很多的,將日常工作的一些事情自動化,對我們的工作效率有很大的提升。

本文面向Py新手,分享一些輔助工作的小工具思路。以下例子都是在Win10 + Py3.5下完成。

調用CMD

subprocess是Python自帶的子進程管理模塊,定義有數個創建子進程的函數,也提供了一些管理標準流(standard stream)和管道(pipe)的工具,從而在進程間使用文本通信。


簡單理解就是,你通過CMD敲的命令,都基本可以用subprocess來實現批量處理。


例子1:批量SVN操作
以更新SVN爲例,這是一個頻繁的操作,尤其是多個SVN目錄需要一一更新的時候,手動起來是挺麻煩的。

import subprocess
subprocess.Popen(r'TortoiseProc.exe /command:update /path:"C:\project\策劃文檔" /closeonend:0')
subprocess.Popen(r'TortoiseProc.exe /command:update /path:"C:\project\配置文檔" /closeonend:0')

例子2:adb命令的封裝
做安卓手遊測試的時候,adb是常用工具,我們可以通過它,進行apk的安裝,卸載,截圖,獲取APK信息,性能數據,獲取手機信息等等操作。

比如獲取當前運行在前臺的apk的package和activity名稱

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

def get_apk_info():
    """獲取apk的package,activity名稱
 :return: list  eg ['com.android.calendar', 'com.meizu.flyme.calendar.AllInOneActivity']
    """
    result = run_cmd("adb shell dumpsys activity top")
    for line in result:
        if line.strip().startswith('ACTIVITY'):
            return line.split()[1].split('/')
print(get_apk_info())


output: ['com.android.calendar', 'com.meizu.flyme.calendar.AllInOneActivity']
比如查看當前apk的內存佔用


def get_mem_using(package_name=None):
    """查看apk的內存佔用


    :param package_name:
    :return: 單位KB
    """
    if not package_name:
        package_name = get_apk_info()[0]
    result = run_cmd("adb shell dumpsys meminfo {}".format(package_name))
    info = re.search('TOTAL\W+\d+', str(result)).group()
    mem = ''
    try:
        mem = info.split()
    except Exception as e:
        print(info)
        print(e)
    return mem[-1]


output: 37769
比如備份當前apk到桌面


def backup_current_apk(path=r"C:\Users\jianbing\Desktop\apks"):
    package = get_apk_info()[0]
    result = run_cmd("adb shell pm path {}".format(package))
    cmd = "adb pull {} {}".format(result[0].split(":")[-1], os.path.join(path, "{}.apk".format(package)))
    print(cmd)
    run_cmd(cmd)
再進一步,將常用的adb操作封裝爲一個ADB工具類。社區裏也有童鞋之前分享過,傳送門。


處理文本


例子:在整個文件夾中搜索關鍵字


某天策劃說,這個版本他刪掉了某個道具,讓我檢查下有沒有刪漏的地方,這個道具產出的地方不少,最佳的檢查方式是各個相關配置表看下還有沒有配置這個道具。


那就寫個腳本遍歷整個文件夾來搜索指定關鍵字吧。


import os

def get_files_by_suffix(path, suffixes=("txt", "xml"), traverse=True):
    """從path路徑下,找出全部指定後綴名的文件


    :param path: 根目錄
    :param suffixes: 指定查找的文件後綴名
    :param traverse: 如果爲False,只遍歷一層目錄
    :return:
    """
    file_list = []
    for root, dirs, files in os.walk(path):
        for file in files:
            file_suffix = os.path.splitext(file)[1][1:].lower()   # 後綴名
            if file_suffix in suffixes:
                file_list.append(os.path.join(root, file))
        if not traverse:
            return file_list


    return file_list




if __name__ == '__main__':


    keyword = "XXX寶箱"


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


    for file in files:


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


            if position != -1:
                print("Find in {0}".format(file))
                start = position - 100 if position - 100 > 0 else 0
                end = position + 100 if position + 100 < len(content) else len(content)
                print(content[start:end])
                print("_" * 100)
操作遠程服務器


例子1:查看內網發版時間


有時候問開發,最近一次內網服務端發版是什麼時候?開發回答:有點忘記了。。那就得自力更生了~


手動方式:使用FTP軟件連入內網服務器,查看文件的更新日期,從而知道發版時間。


懶人方式:Py大法好~


paramiko是Python很有名的第三方庫,遵循SSH2協議,支持以加密和認證的方式,進行遠程服務器的連接。


import paramiko
import time


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


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


sftp.close()
例子2:查看內網報錯信息


在進行測試的時候,需要多留意服務端是否有新的報錯信息,有些報錯在客戶端並沒有什麼表現,比如數據進庫失敗,手動方式:通過SecureCRT連入內網服務器,CD到Log目錄下,然後tail -n 200 sg_error.log 查看最新的報錯信息。


於是萌生了寫一個小工具來定時檢測,發現報錯信息就保存起來的想法。


import datetime
import paramiko
import time
import os




class ScanError(object):


    def __init__(self):


        self._ssh = paramiko.SSHClient()
        self.last_error_log = None
        self._init()


    def _init(self):
        os.chdir("data")   # 打算將報錯信息保存到data目錄下
        self._ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self._ssh.connect("192.168.1.10", username="root", password="XXXXXX")


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


        # 檢測最近三天有沒有報錯信息
        today = datetime.date.today()
        yesterday = today - datetime.timedelta(days=1)
        the_day_before_yesterday = today - datetime.timedelta(days=2)


        error_log_str = "\n".join(error_log)
        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:
            self.save_error_log("error.txt", error_log)
            print('內網最近三天有錯誤信息,請查看')
            os.popen('error.txt')


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

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

   def run_forever(self, interval=30, show_error=True):
        """運行檢測工具


        :param interval: 檢測間隔
        :param show_error: 是否檢測到報錯就自動彈出顯示
        :return:
        """
        while 1:
            time.sleep(interval)
            error_log = self.get_error_log()


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


                self.last_error_log = error_log
                file_name = time.strftime("%Y-%m-%d-%H-%M-%S.txt", time.localtime(time.time()))
                print('{} 檢測到內網有新的錯誤信息'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))))
                self.save_error_log(file_name, error_log)


                if show_error:
                    os.popen(file_name)
if __name__ == '__main__':
    ScanError().run_forever()
數據庫操作


例子:找號功能


內網的服務器建了N多的號,有時候看着排行榜某個帳號,想登錄看下數據,可以使用Python寫一個連接數據庫的找號腳本。


import cymysql


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


sql = "select * from Player where name like '%{0}%'".format(player_name)   # 模糊搜索,從玩家名稱搜索玩家ID
cur.execute(sql)
for r in cur.fetchall():
    sql = "select * from Account where uid = '{0}'".format(r[0])   # 從玩家ID搜索玩家帳號
    cur.execute(sql)
    for row in cur.fetchall():
        print('{0}, {1}, {2}'.format(r[0], r[1], row[2]))  # 打印相關信息


conn.close()
擴展開發提供的工具


在之前某個項目,開發做了一個給遊戲帳號發道具的網頁,提供測試使用,操作流程是這樣的,在網頁上的表單裏邊,填寫玩家的ID,在下拉列表選中要發送的道具(支持模糊搜索),填寫數量。


這個網頁使用起來,工作效率不高的地方就是,每次添加道具,都需要重新選擇道具和填寫數量,且添加過程沒有記錄下來,無法複用。


優化方案,在網頁上點擊添加道具,其實就是網頁給遊戲服務器發送了一個HTTP請求,那就直接讓Python來代勞吧~


import requests


player_id = 1100000103
server_ip = "192.168.1.21:5000"


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


data["stuffList"].append({"itemID": 1104000007, "number": 1000})  # itemID爲1104000007的物品,數量1000
data["stuffList"].append({"itemID": 1104000008, "number": 1000})
data["stuffList"].append({"itemID": 1104000009, "number": 1000})
data["stuffList"].append({"itemID": 1104000010, "number": 1000})
data["stuffList"].append({"itemID": 1104000011, "number": 1000})
data["stuffList"].append({"itemID": 1104000012, "number": 1000})


requests.post(url, json=data, timeout=5)    # 添加道具
# requests.delete(url, json=data, timeout=5)   # 刪除道具 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章