playtest — автоматизированное тестирование боевых ядер

контрольная работа

Эта статья была впервые опубликована на:Уокер ИИ

Сегодня я поделюсь с вами реализацией теста автоматизации игры. В этой игре есть самостоятельное боевое ядро, отвечающее за расчет внутриигровых боёв, поэтому каждый раз, когда нужно тестировать боевое ядро, нужно переустанавливать сервер, перепаковывать клиент (мобильный, ПК и т.д.), и наконец доставили на тест для тестирования, весь процесс долгий и трудоёмкий, поэтому мы рассматриваем тестирование при обновлении боевого ядра, что может упростить процесс тестирования и сэкономить время.

После обсуждения с группой разработки ядра я решил использовать инструмент QT, предоставленный группой разработки ядра (как показано на рисунке ниже), чтобы запустить боевое ядро ​​игры локально, построить тестовую сцену, выполнив несколько команд, а затем получить тест посредством взаимодействия с данными, чтобы достичь желаемых целей тестирования.

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] и обсудите с ходоками!