This article was first published on:Walker AI

Today to share a game automation test landing. This game has its own combat the calculation of the kernel is responsible for the local battle, so every time need to test the combat kernel, redeploy all need server, the client (mobile and PC, etc.) to the package, the final test can be delivered to the test, the whole process longer, also more time-consuming, so we will consider kernel update is tested in a fight, This simplifies the testing process and saves time.

After discussion with the kernel group, we decided to use the QT tool provided by the kernel development group (as shown in the figure below) to run the battle kernel of the game locally, build the testing scene by executing multiple commands, and then get the testing data through data interaction, so as to achieve the testing purpose we wanted.

1. Environment preparation

  • Windows10

  • Python3.7

  • Allure

  • Python third-party libraries: PyTest, allure- PyTest, Python-gitLab, Zipp, XLRD

2. Implementation process

2.1 Communication Mode

When QT is started, the startup parameters include the communication port number. After QT is started, python is used to establish socket connection with QT locally for communication.

    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) :
        """ establish a connection """
        localhost = socket.gethostbyname(socket.gethostname())
        self.sock.connect({localhost, self.port})
Copy the code

The operation command of each use case is stored in the list, and the command is sent by traversing the list. After the JSON serialization of each command, the command is sent through socket connection. After the sending, the command is judged and the next operation is performed.

    def send(self, data) :
        """ Send command """
        for d in data:
            if 'sleep' == d.keys():
                Keep the socket connected and send heartbeat data to QT
                self.hearbeats(int(d['sleep'] [0]), int(d['sleep'] [1]))
            else:
                _ = json.dumps(d).encode(encoding='utf-8')
                self.sock.send(_)
                Multiple commands should be sent at an interval of 0.3s
                time.sleep(0.3)
                After sending the initialization command, wait for the QT data to initialize
                if d['Pack-Field'] = ='client.initserver':
                    time.sleep(5)
                # when hread-field ==2, request current game data from the kernel
                if d['Hread-Field'] = ='2':
                    # 8 player stats +1 game scene stats
                    self.data = [self.recv_game_data() for _ in range(9)]
Copy the code

Receives the data, the first 8 bytes for the size of the package, is behind the data, we need for testing is locally, so there is no to encrypt data, the received data can be directly converted to json format, when receiving data received in QT data sending a heartbeat, after removing the heart data, is that we want the game data.

    def recv_game_data(self) :
        """ Receive game data, remove heartbeat data """
        data = self.recv_data()
        Hread-field ==Heartbeat indicates that the data is a 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(_)
Copy the code

2.2 Use case design

Use cases are uniformly stored in Excel files, each Sheet is a game mode, and each table contains use case numbers, descriptions, states, steps and data verification. During the execution of use cases, commands are sent to QT in accordance with the pre-designed steps to build test scenarios. After receiving the data returned by QT, it is compared with the data in data verification to verify the correctness of kernel functions.

Use case number Use case description Use case status Test steps Data validation
Base Gold -001 If you win a match, you get +1 for victory gold and +5 for base gold perform client.initserver,

Client. SetRound. 4.0,

2,

Client. AddChessToMap. 0.110011.0.0,

client.goNextStage,

4.5, sleep.

2
{“data_2”:{“player_0”:{“gold_increase”:6},”GameMode”:0}}

2.3 Data Verification

The data returned by QT each time contains 8 player data and 1 game scene data, so we extract the corresponding fields in the returned data for comparison and verification according to the verification fields declared in the use case, and the specific implementation code is as follows:

    def format_data(self) :
        """ Format the received data """
        for _ in self.received_data.keys():
            data = self.received_data[_]
            self.received_data_formatted[_] = dict(a)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) :
        "" perform data verification. ""
        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:
                            Execute the corresponding verification module
                            eval(f'self.{check_type_2}() ')
                        except AssertionError as e:
                            Catch and record the exception returned by the verification module
                            self.failed_dict[check_type_2] = e.args
Copy the code

Data verification fields such as gold_increase and GameMode correspond to a verification module, gold_increase refers to the current increase of gold, GameMode refers to the current GameMode, and of course there are many verification modules such as: HandleChess, HandleEquip, ChessBoard, BoardEquip… , etc.

    def gold_increase(self) :
        # Check increased value of coins
        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
        ifincrease_num ! =int(assert_data):
            raise AssertionError(f'{self.data_key}-{self.check_type}: {increase_num}! ={assert_data}')
    
    def GameMode(self, data_key) :
    Verify the current game mode
    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}'
Copy the code

2.4 Kernel version update

Use the python-gitlab library to download files from the specified kernel branch on Gitlab. After downloading, replace the old files to update the local kernel version and install the python-gitlab library.

pip install python-gitlab
Copy the code
import gitlab
import os
import time
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.split(curPath)[0]

class DownloadFiles:
    """ Download the specified branch file from 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) Project ID #
        root_path = f'{rootPath}/resource/'
        info = project.repository_tree(all=True, recursive=True, as_list=True, ref=self.version)
        file_list = list(a)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 related file not detected, please upload ")
            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!")
Copy the code

3. Deficiencies

There are still some shortcomings in kernel automation testing:

  • The writing of use cases is complicated. First of all, you need to be familiar with the command, and then the field name of the verification, and the format of the data verification is required.

  • The current version only runs on Windows, preferably directly on the test server.

  • If the C++ code changes, you need to manually update QT;

If you find any other problems, please feel free to point them out in the comments section. I also hope to share with friends who are interested in game automation testing related methods and experience.


PS: more dry technology, pay attention to the public, | xingzhe_ai 】, and walker to discuss together!