Эта статья была впервые опубликована на:Уокер ИИ
Сегодня я поделюсь с вами реализацией теста автоматизации игры. В этой игре есть самостоятельное боевое ядро, отвечающее за расчет внутриигровых боёв, поэтому каждый раз, когда нужно тестировать боевое ядро, нужно переустанавливать сервер, перепаковывать клиент (мобильный, ПК и т.д.), и наконец доставили на тест для тестирования, весь процесс долгий и трудоёмкий, поэтому мы рассматриваем тестирование при обновлении боевого ядра, что может упростить процесс тестирования и сэкономить время.
После обсуждения с группой разработки ядра я решил использовать инструмент QT, предоставленный группой разработки ядра (как показано на рисунке ниже), чтобы запустить боевое ядро игры локально, построить тестовую сцену, выполнив несколько команд, а затем получить тест посредством взаимодействия с данными, чтобы достичь желаемых целей тестирования.
1. Подготовка окружающей среды
-
Windows10
-
Python3.7
-
Allure
-
Сторонние библиотеки Python: pytest, allure-pytest, python-gitlab, zipp, xlrd.
2. Процесс реализации
2.1 Способ связи
При запуске QT номер порта для связи включается в параметры запуска. После запуска QT используйте python и QT для локального установления соединения через сокет для связи.
def __init__(self, port):
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.data = {}
def connect(self):
"""
建立连接
"""
localhost = socket.gethostbyname(socket.gethostname())
self.sock.connect({localhost, self.port})
Сохраните рабочие команды каждого варианта использования в списке и пройдите по списку, чтобы отправить команды.После сериализации каждой команды с помощью json она отправляется через соединение сокета.После завершения отправки отправленные команды оцениваются, и следующий шаг сделан.
def send(self, data):
"""
发送命令
"""
for d in data:
if 'sleep' == d.keys():
# 保持socket连接,向QT发送心跳数据
self.hearbeats(int(d['sleep'][0]), int(d['sleep'][1]))
else:
_ = json.dumps(d).encode(encoding='utf-8')
self.sock.send(_)
# 多条命令发送需要间隔0.3s
time.sleep(0.3)
# 发送初始化命令之后,等待QT数据初始化
if d['Pack-Field'] == 'client.initserver':
time.sleep(5)
# 当Hread-Field==2时表示向内核请求当前游戏数据
if d['Hread-Field'] == '2':
# 8个玩家数据+1个游戏场景数据
self.data = [self.recv_game_data() for _ in range(9)]
При получении данных первые 8 байт - это размер пакета, а вторые - это данные, которые нам нужны.Поскольку тест выполняется локально, данные не шифруются. Полученные данные могут быть напрямую преобразованы в формат json. Данные получены, будут получены данные сердцебиения, отправленные QT.После того, как данные сердцебиения будут удалены, это игровые данные, которые нам нужны.
def recv_game_data(self):
"""
接收游戏数据,去除心跳数据
"""
data = self.recv_data()
# 当Hread-Field==Heartbeat时表示数据为心跳数据
while data['Hread-Field'] == 'Heartbeat':
data = self.recv_data()
else:
return data
def recv_data(self):
size = struct.unpack('q', self.recv(8))[0]
data = json.loads(self.recv(size).decode('utf-8'))
return data
def recv(self, size):
n = 0
data = b''
while n < size:
_ = self.sock.recv(size - n)
data += _
n += len(_)
2.2 Дизайн варианта использования
Варианты использования единообразно хранятся в файле Excel, каждый лист представляет собой игровой режим, и каждый лист содержит номер варианта использования, описание, статус, шаги и проверку данных. Когда вариант использования выполняется, он отправляет команды QT для построения тестового сценария в соответствии с заранее разработанными шагами, и после получения данных, возвращаемых QT, они сравниваются с данными в проверке данных для проверки правильности функция ядра.
номер варианта использования | описание варианта использования | статус варианта использования | шаги теста | Проверка достоверности данных |
---|---|---|---|---|
Базовая золотая монета-001 | Если игра выиграна, золотая монета победы +1, базовая золотая монета +5. | воплощать в жизнь | client.initserver, client.setRound.4.0, 2, client.addChessToMap.0.110011.0.0, client.goNextStage, sleep.4.5, 2 |
{"data_2":{"player_0":{"gold_increase":6},"GameMode":0}} |
2.3 Проверка данных
Каждый раз, когда данные, возвращаемые QT, содержат данные 8 игроков и данные 1 игровой сцены, поэтому мы извлекаем соответствующие поля из возвращаемых данных для сравнения и проверки в соответствии с полями проверки, объявленными в варианте использования.Конкретный код реализации выглядит следующим образом:
def format_data(self):
"""
格式化接收到的数据
"""
for _ in self.received_data.keys():
data = self.received_data[_]
self.received_data_formatted[_] = dict()
for player_num in range(len(data)):
player_data = data[player_num]
if player_num == 8:
self.received_data_formatted.get(_)['game_scene'] = player_data
else:
self.received_data_formatted.get(_)[f'player_{player_num}'] = player_data
def verify_data(self):
"""
进行数据校验
"""
self.format_data()
for data_key in self.assert_data.keys():
self.data_key = data_key
for check_type_1 in self.assert_data[data_key].keys():
self.check_type = check_type_1
if check_type_1 == 'GameMode':
try:
self.GameMode(data_key)
except AssertionError as e:
self.failed_dict['GameMode'] = e.args
else:
for check_type_2 in self.assert_data[data_key][check_type_1]:
try:
# 执行对应的校验模块
eval(f'self.{check_type_2}()')
except AssertionError as e:
# 捕获校验模块返回的异常并记录
self.failed_dict[check_type_2] = e.args
Все поля в проверке данных, такие как gold_increase и GameMode, соответствуют модулю проверки, gold_increase относится к текущему увеличению золотых монет игрока, GameMode относится к текущему игровому режиму, и, конечно же, существует множество модулей проверки, таких как : HandleChess, HandleEquip, ChessBoard, BoardEquip и т. д.
def gold_increase(self):
# 校验金币增加值
assert_data = self.assert_data[self.data_key][self.check_type]['gold_increase']
received_data_1 = self.received_data_formatted[f'data_{int(self.data_key[-1:]) - 1}'][self.check_type]['Pack-Field']['k_19']
received_data_2 = self.received_data_formatted[self.data_key][self.check_type]['Pack-Field']['k_19']
increase_num = received_data_2-received_data_1
if increase_num != int(assert_data):
raise AssertionError(f'{self.data_key}-{self.check_type}: {increase_num} != {assert_data}')
def GameMode(self, data_key):
# 校验当前游戏模式
game_mode_assert = self.assert_data[data_key]['GameMode']
game_mode_received = self.received_data_formatted.get(data_key)['game_scene']['Pack-Field']['k_0']
assert game_mode_received == game_mode_assert,\
f'{self.data_key}-{self.check_type}: {game_mode_received} != {game_mode_assert}'
Обновление версии ядра 2.4
Используйте библиотеку python-gitlab для загрузки файлов в указанной ветке ядра на gitlab и замените старые файлы после завершения загрузки, чтобы обновить локальную версию ядра и установить библиотеку python-gitlab.
pip install python-gitlab
import gitlab
import os
import time
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.split(curPath)[0]
class DownloadFiles:
"""
从gitlab上下载指定分支文件
"""
def __init__(self, version):
self.dir_name = None
self.version = version
def create_dir(self):
if not os.path.isdir(self.dir_name):
os.makedirs(self.dir_name)
time.sleep(0.1)
def start_get(self):
gl = gitlab.Gitlab.from_config('xiaoming', [f'{curPath}/git.ini'])
gl.projects.list()
project = gl.projects.get(1234) # 项目ID
root_path = f'{rootPath}/resource/'
info = project.repository_tree(all=True, recursive=True, as_list=True, ref=self.version)
file_list = list()
if not os.path.isdir(root_path):
os.makedirs(root_path)
os.chdir(root_path)
for info_dir in range(len(info)):
if info[info_dir]['type'] == 'tree':
self.dir_name = info[info_dir]['path']
self.create_dir()
else:
file_name = info[info_dir]['path']
# logger.info(file_name)
if file_name == 'windows.zip':
file_list.append(file_name)
if 'Datas_jit' in file_name:
file_list.append(file_name)
if 'Datas_jit/' not in str(file_list):
# raise ValueError("未检测到Datas相关文件,请上传")
pass
for info_file in range(len(file_list)):
get_file = project.files.get(file_path=file_list[info_file], ref=self.version)
content = get_file.decode()
with open(file_list[info_file], 'wb') as code:
logger.info(f"START-DOWNLOAD: {file_list[info_file]}")
code.write(content)
logger.info(f"DOWNLOAD COMPLETE!")
3. Недостатки
В текущем тесте автоматизации ядра все еще есть некоторые недостатки:
-
Написание варианта использования более сложное: во-первых, вам нужно знать команду, затем имя поля для проверки, а также требуется формат проверки данных;
-
Текущая версия может работать только в среде Windows, лучше всего, если она может работать непосредственно на тестовом сервере;
-
Если код C++ изменен, вам необходимо вручную обновить QT;
Если вы также обнаружите другие проблемы, укажите их в области комментариев, а также надейтесь на обмен методами и опытом, связанными с тестированием автоматизации игр, с друзьями, которые заинтересованы в тестировании автоматизации игр.
PS: Для получения дополнительной технической галантереи, пожалуйста, обратите внимание на [Публичный аккаунт | xingzhe_ai] и обсудите с ходоками!