A must-read article series in 2024! ! ! Interface automated testing framework ideas and practice (5): [Recommended] Hybrid test automation framework (keyword + data driven)

Hybrid test automation framework (keyword + data driven)

Keyword-driven or table-driven testing framework

  This framework requires the development of data tables and keywords. These data tables and keywords are independent of the test automation tool that executes them, and can be used to "drive" the test script code of the application and data under test. Keyword-driven testing looks very similar to manual test cases. In a keyword-driven test, the functions of the application under test are written into a table together with the execution steps of each test.

       This testing framework can generate a large number of test cases with very little code. The same code is reused while using the data table to generate individual test cases.

Hybrid test automation framework

  The most common execution framework is a combination of all the techniques introduced above, taking their strengths and making up for their weaknesses. This hybrid testing framework evolved from most frameworks over time and through several projects.

Unittest details about the test report showing the use case name:

There are two built-in properties in unittest, you can customize the use case name

Previous effect:

 

Use built-in attribute effects:

 

Framework design goals

  The designed framework is given directly to testers, and other testers only need to simply add test cases to it continuously; therefore, our framework design must be simplified in three ways: simple operation, simple maintenance, and simple expansion.

  When designing the framework, it must be combined with the business process, not just relying on technology to achieve it. In fact, technology implementation is not difficult, but the understanding and grasp of the business process is difficult.

  When designing the framework, the basic ones should be encapsulated into public ones, such as get requests, post requests and assertions into the same basic general class.

  Test cases should be separated from the code to facilitate use case management and implemented using a data-driven framework.

As shown below:

  By entering test cases in Excel, the test cases are automatically executed after the framework is run, and an HTML web page version of the test report is generated.

Report results:

Technical points used in the framework

1. Language: python

2. Test framework: unittest (assertEqual) or pytest

3. Interface call: requests (API is very simple)

4. Data-driven: paramunittest (parameterization can be achieved by assembling data in a certain format)

5. Data management: xlrd (read excel file data), configparser (read configuration file)

6. Data format conversion: ast, json

7. Log processing: logging --- clear execution process and quick problem location

8. Test report: HTMLTestReportCN (produced and designed by netizens, the display is clear and beautiful)

9. Test email sending test report: smtplib (email content format setting), email (sending and receiving emails)

10. Continuous integration: Jenkins (execute interface test script according to strategy)

(Recommended) Hybrid test automation framework (keyword + data driven)

Data source implementation:

The data source currently uses excel, and the data is as follows:

Link: https://pan.baidu.com/s/1VvvGYRvGbElSlP6ngg0ktw
Extraction code: ppua

 Recommended tutorials related to automated testing:

The latest automated testing self-study tutorial in 2023 is the most detailed tutorial for newbies to get started in 26 days. Currently, more than 300 people have joined major companies by studying this tutorial! ! _bilibili_bilibili

2023 latest collection of Python automated test development framework [full stack/practical/tutorial] collection essence, annual salary after learning 40W+_bilibili_bilibili

Recommended tutorials related to test development

The best in the entire network in 2023, the Byte test and development boss will give you on-site teaching and teach you to become a test and development engineer with an annual salary of one million from scratch_bilibili_bilibili

postman/jmeter/fiddler test tool tutorial recommendation

The most detailed collection of practical tutorials on JMeter interface testing/interface automated testing projects. A set of tutorials for learning jmeter interface testing is enough! ! _bilibili_bilibili

To teach yourself how to capture packets with fiddler in 2023, please be sure to watch the most detailed video tutorial on the Internet [How to Learn to Capture Packets with Fiddler in 1 Day]! ! _bilibili_bilibili

In 2023, the whole network will be honored. The most detailed practical teaching of Postman interface testing at Station B can be learned by novices_bilibili_bilibili

Idea: Use python to read excel data; use xlrd3

Frame 01: New project API_KEY_WORD_TEST_FRAME;

Step 1. Create a new common py folder and a conf ordinary folder in the project root directory; the samples folder is a demo used to write test code;

Step 2. Create a new config.ini file under conf

 Write code:

[default]

# Main aircraft site
hosts = api.weixin.qq.com
Step 3. Create new ini_file_utils.py files and config_utils.py files under common

The ini_file_utils.py file is as follows:

 Summarize:

 Optical theory is useless. You must learn to follow along and practice it in order to apply what you have learned to practice. At this time, you can learn from some practical cases.

If it is helpful to you, please like and save it to give the author an encouragement. It also makes it easier for you to search quickly next time.

If you don’t understand, please consult the small card below. The blogger also hopes to learn and improve with like-minded testers.

At the appropriate age, choose the appropriate position and try to give full play to your own advantages.

My path to automated test development is inseparable from plans at each stage, because I like planning and summarizing.

Test development video tutorials and study notes collection portal! !

 Write code:

Copy code

# encoding: utf-8
# @author: Jeffrey
# @file: ini_file_utils.py
# @time: 2022/8/4 22:23
# @desc: 读取、写入ini文件
import os
import configparser


class IniFileUtils:  #和框架业务无关的底层代码==》公共底层代码

    def __init__(self,file_path):
        self.ini_file_path = file_path
        self.conf_obj = configparser.ConfigParser()
        self.conf_obj.read(self.ini_file_path, encoding='utf-8')

    def get_config_value(self,section, key):
        value = self.conf_obj.get(section, key)
        return value

    def set_config_value(self,section, key, value):
        '''设置config.ini文件中的值'''
        self.conf_obj.set(section, key, value)
        config_file_obj = open(self.ini_file_path, 'w')
        self.conf_obj.write(config_file_obj)
        config_file_obj.close()

if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    config_file_path = os.path.join(current_path, '../conf/config.ini')
    ini_file = IniFileUtils(config_file_path)
    print(ini_file.get_config_value('default', 'HOSTS'))

Copy code

The config_utils.py file is as follows:

 Write code:

Copy code

# encoding: utf-8
# @author: Jeffrey
# @file: config_utils.py
# @time: 2022/8/4 22:26
# @desc: 封装读取ini文件的方法

import os
from common.ini_file_utils import IniFileUtils


current_path = os.path.dirname(os.path.abspath(__file__))
config_file_path = os.path.join(current_path, '../conf/config.ini')


class LocalConfig():  # #和框架业务有关系的底层代码

    def __init__(self,file_path = config_file_path):
        self.ini_file_obj = IniFileUtils(file_path)

    @property
    def get_hosts(self):
        '''获取ini文件中的hosts值'''
        hosts_value = self.ini_file_obj.get_config_value('default', 'hosts')
        return hosts_value


local_config = LocalConfig()

if __name__ == '__main__':
    print(local_config.get_hosts)

Copy code

Step 4. Write a linear script under the samples file to read the merged cells in excel

The Excel table is as follows:

 

 

 

 Write code:

Copy code

# encoding: utf-8
# @author: Jeffrey
# @file: demo01.py
# @time: 2022/8/7 14:53
# @desc: Reading of merged cells in excel
import xlrd3


work_book = xlrd3.open_workbook('test_data.xlsx') # Create a workbook object
sheet_obj = work_book.sheet_by_name('Sheet1') # Create a table object
print(sheet_obj.cell_value(1,2)) # Get the value of the cell (row, column), starting from 0, the actual serial number of the row and column is -1
print(sheet_obj.cell_value(7,3)) # Get the value of the cell

print(sheet_obj.cell_value(1,0)) # Merged cells, the obtained value is empty
print(sheet_obj.cell_value(7,0)) # Merged cells, the obtained value is empty

# Contains four elements (starting row, ending row, actual column, ending column), including the front and not the end
print(sheet_obj.merged_cells) # [(1, 5, 0, 1), (5, 9, 0, 1)]
# Idea step 1: Determine whether a cell is a merged cell
x = 3; y = 0
if x>=1 and x<5:
    if y>=0 and y<1:
        print('Merge cells')
    else:
        print('non-merged cells')
else:
    print('Non-merged cells')

# Idea step 2: How to write a for loop
for (min_row,max_row,min_col,max_col) in [(1, 5, 0, 1), (5, 9, 0, 1)]:
    print(min_row,max_row,min_col,max_col)

# Idea step 3: Integrate ideas 1 and 2 into cells to determine whether they are merged cells
x = 6; y = 0
for (min_row,max_row,min_col,max_col) in sheet_obj.merged_cells:
    if x >= min_row and x < max_row:
        if y >= min_col and y < max_col:
            print('Merge cells')
            break
        else:
            print('non-merged cells')
    else:
        print('Non-merged cells')

# Idea step 4: Let the values ​​of the merged cells be equal to the value of the first merged cell, and the non-merged cells will be the original values
x = 4; y = 1
cell_value = None
for (min_row,max_row,min_col,max_col) in sheet_obj.merged_cells:
    if x >= min_row and x < max_row:
        if y >= min_col and y < max_col:
            cell_value = sheet_obj.cell_value(min_row,min_col)
            break
        else:
            cell_value = sheet_obj.cell_value(x, y)
    else:
        cell_value = sheet_obj.cell_value(x, y)
print(cell_value)

#Make the appeal code into a method
def get_merged_cell_value(row_index,col_index):
    cell_value = None
    for (min_row, max_row, min_col, max_col) in sheet_obj.merged_cells:
        if row_index >= min_row and row_index < max_row:
            if col_index >= min_col and col_index < max_col:
                cell_value = sheet_obj.cell_value(min_row, min_col)
                break
            else:
                cell_value = sheet_obj.cell_value(row_index, col_index)
        else:
            cell_value = sheet_obj.cell_value(row_index, col_index)
    return cell_value

print(get_merged_cell_value(8,0))


# 获取excel中所有的数据 线性脚本
# 步骤一:线性脚本
head = sheet_obj.row_values(0)
print(head)  # ['学习课程', '步骤序号', '步骤操作', '完成情况']
excel_list = []
excel_dict = {}
excel_dict[head[0]] = get_merged_cell_value(1,0)
excel_dict[head[1]] = get_merged_cell_value(1,1)
excel_dict[head[2]] = get_merged_cell_value(1,2)
excel_dict[head[3]] = get_merged_cell_value(1,3)
excel_list.append(excel_dict)
print(excel_list)

# 步骤二: 使用for循环封装
head = sheet_obj.row_values(0)
excel_data_list = []
for j in range(1,sheet_obj.nrows):
    row_value_dict = {}
    for i in range(sheet_obj.ncols):
        row_value_dict[head[i]] = get_merged_cell_value(j,i)
    excel_data_list.append(row_value_dict)
print(excel_data_list)

for data in excel_data_list:
    print(data)

Copy code

Step 5. Create a new excel_file_utils.py file under common

 

 Write code:

Copy code

# encoding: utf-8
# @author: Jeffrey
# @file: excel_file_utils.py
# @time: 2022/8/7 15:52
# @desc: 封装读取excel文件
import os
import xlrd3


class ExcelFileUtils():

    def __init__(self,excel_file_path, sheet_name):
        self.excel_file_path = excel_file_path
        self.sheet_name = sheet_name
        self.sheet_obj = self.get_sheet()

    def get_sheet(self):
        '''根据excel路径已经表名称 创建一个表格对象'''
        workbook = xlrd3.open_workbook(self.excel_file_path)
        sheet = workbook.sheet_by_name(self.sheet_name)
        return sheet

    def get_row_count(self):
        '''获取表格实际行数'''
        row_count = self.sheet_obj.nrows
        return row_count

    def get_col_count(self):
        '''获取表格的实际列数'''
        col_count = self.sheet_obj.ncols
        return col_count

    def get_merged_cell_value(self,row_index, col_index):
        '''
        :param row_index: 行下标
        :param col_index: 列下标
        :return: 获取单元格的内容
        '''
        cell_value = None
        for (min_row, max_row, min_col, max_col) in self.sheet_obj.merged_cells:
            if row_index >= min_row and row_index < max_row:
                if col_index >= min_col and col_index < max_col:
                    cell_value = self.sheet_obj.cell_value(min_row, min_col)
                    break
                else:
                    cell_value = self.sheet_obj.cell_value(row_index, col_index)
            else:
                cell_value = self.sheet_obj.cell_value(row_index, col_index)
        return cell_value

    def get_all_excel_data_list(self):
        '''获取excel中所有的数据,以列表嵌套字典的形式'''
        excel_data_list = []
        head = self.sheet_obj.row_values(0)
        for j in range(1,self.get_row_count()):
            row_value_dict = {}
            for i in range(self.get_col_count()):  # sheet_obj.ncols 动态获取表格多少列
                row_value_dict[head[i]] = self.get_merged_cell_value(j,i)
            excel_data_list.append(row_value_dict)
        return excel_data_list


if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    file_path = os.path.join(current_path, '../samples/test_data.xlsx')
    excel_obj = ExcelFileUtils(file_path, 'Sheet1')
    print(excel_obj.get_all_excel_data_list())
    print(excel_obj.get_col_count())

Copy code

Test execution results:

Step 6. Create a new test_data ordinary folder in the project root directory and put the test case files in it.

 

Step 7. Create a new testcase_data_utils.py file under common to convert the test case data into the format required by the framework.

 Write code:

Copy code

# encoding: utf-8
# @author: Jeffrey
# @file: testcase_data_utils.py
# @time: 2022/8/7 17:02
# @desc: Data format conversion: Convert test case data into the format required by the framework
import os
from common.excel_file_utils import ExcelFileUtils

current_path = os.path.dirname(__file__)
file_path = os.path.join(current_path, '../test_data/testcase_infos.xlsx')


class TestCaseDataUtils:

    def __init__(self):
        self.test_data = ExcelFileUtils(excel_file_path=file_path,
                                        sheet_name='Sheet1')\
            .get_all_excel_data_list()

    def convert_testcase_data_to_dict(self):
        '''Convert the test case data in excel to [{},{},{}] to {"":[],"":[], "":[]}'''
        test_case_data_dict = {}
        for row_data in self.test_data:
            test_case_data_dict.setdefault( row_data['test case number'],[] )\
                .append(row_data)
        return test_case_data_dict

    def convert_testcase_data_to_list(self):
        '''Convert the data {"":[],"":[],"":[]} into ["case_id":key," ;case_step":value]'''
        test_case_data_list = []
        for key, value in self.convert_testcase_data_to_dict().items():
            case_info_dict = {}
            case_info_dict['case_id'] = key
            case_info_dict['case_step'] = value
            test_case_data_list.append(case_info_dict)
        return test_case_data_list




if __name__ == '__main__':
    # print(TestCaseDataUtils().convert_testcase_data_to_dict())
    print(TestCaseDataUtils().convert_testcase_data_to_list())

Copy code

The purpose of the convert_testcase_data_to_dict() method is to separate the use case data by use case number and use case data

'''Convert the test case data in excel to [{},{},{}] to {"":[],"":[], "":[]}'''

View execution results:

The purpose of the convert_testcase_data_to_list() method is to separate the use case numbers and use case steps

'''Convert the data {"":[],"":[],"":[]} into ["case_id":key," ;case_step":value]'''

converted data

Framework 02, encapsulate requests requests, create a new requests_utils.py file under common

Step 1, encapsulate the get request

 Write code:

Copy code

# encoding: utf-8
# @author: Jeffrey
# @file: requests_utils.py
# @time: 2022/8/7 21:42
# @desc: Encapsulate requests requests
import json

import requests
from common.config_utils import local_config


class RequestsUtils:

    def __init__(self):
        self.hosts = local_config.get_hosts
        self.session = requests.session()

    def get(self,requests_info):

        url = 'https://%s%s' % (self.hosts, requests_info['Request Address'])
        # Use json.loads() to convert the string into a dictionary
        url_params = json.loads(requests_info['Request Parameters (get)'])
        # Use the ternary operator to determine if the request header information is empty and return an empty dictionary {}
        header_info = (json.loads(requests_info['Request header information'])
                            if requests_info['Request header information'] else {} )
        response = self.session.get(url=url, params=url_params, headers=header_info)
        response.encoding = response.apparent_encoding # Prevent response text from being garbled
        print(response.text)


if __name__ == '__main__':
    requests_info = {'Request header information': '',
                     'Request address': '/cgi-bin/token',
                     'Request parameters (get)': '{"grant_type":"client_credential",'
                                  '"appid":"wxf1856",'
                                  '"secret":"92a113bd423c99"}'}
    result = RequestsUtils().get(requests_info)
    print(result)

Copy code

Step 2. Use all the returned response information as a dictionary with an additional code field.

 Write code:

Copy code

result = {
    'code':0,
    'response_code':response.status_code,
    'response_info':response.reason,
    'response_headers':response.headers,
    'response_body':response.text }

Copy code

Step 3. Encapsulate post request

Guess you like

Origin blog.csdn.net/m0_59868866/article/details/134511544