目录:导读
- 前言
- 一、Python编程入门到精通
- 二、接口自动化项目实战
- 三、Web自动化项目实战
- 四、App自动化项目实战
- 五、一线大厂简历
- 六、测试开发DevOps体系
- 七、常用自动化测试工具
- 八、JMeter性能测试
- 九、总结(尾部小惊喜)
 
前言
1、关键字驱动框架简介
原理及特点:
 ①关键字驱动测试是数据驱动测试的一种改进类型,它也被称为表格驱动测试或者基于动作字的测试。
 ②主要关键字包括三类:被操作对象(Item)、操作行为(Operation)和操作值(Value),用面向对象形式可将其表现为 Item.Operation(Value)。
③将测试逻辑按照这些关键字进行分解,形成数据文件。
④用关键字的形式将测试逻辑封装在数据文件中,测试工具只要能够解释这些关键字即可对其应用自动化。
优势:
 ①执行人员可以不需要太多的技术:一旦框架建立,手工测试人员和非技术人员都可以很容易的编写自动化测试脚本。
②简单易懂:它存在 Excel 表格中,没有编码,测试脚本容易阅读和理解。关键字和操作行为这样的手工测试用例,使它变得更容易编写和维护。
③早期介入:可以在应用未提交测试之前,就可以建立关键字驱动测试用例对象库,从而减少后期工作。使用需求和其它相关文档进行收集信息,关键字数据表可以建立手工测试程序。
④代码的重用性:用关键字的形式将测试用例及数据进行组装并解释执行,提高代码的可重用性。
2、框架结构说明
框架结构:
 整个测试框架分为四层,通过分层的方式,测试代码更容易理解,维护起来较为方便。
第一层是“测试工具层”:
 util 包:用于实现测试过程中调用的工具类方法,例如读取配置文件、页面元素的操作方法、操作 Excel 文件、生成测试报告、发送邮件等。
 conf 包:配置文件及全局变量。
 log 目录:日志输出文件。
 exception_pic 目录:失败用例的截图保存目录。
第二层是“服务层”:
 相当于对测试对象的一个业务封装。对于接口测试,是对远程方法的一个实现;对于 UI 测试,是对页面元素或操作的一个封装。
 action 包:封装具体的页面动作,如点击、输入文本等。
第三层是“测试用例逻辑层”:
 该层主要是将服务层封装好的各个业务对象,组织成测试逻辑,进行校验。
bussiness_process 包:基于关键字的形式,实现单条、多条用例的测试脚本逻辑。
 test_data 目录:Excel 数据文件,包含用例步骤、被操作对象、操作动作、操作值、测试结果等。
第四层是“测试场景层”:
 将测试用例组织成测试场景,实现各种级别 cases 的管理,如冒烟,回归等测试场景。
main.py:本框架工程的运行主入口。



框架特点:
 ①基于关键字测试框架,即使不懂开发技术的测试人员也可以实施自动化测试,便于在整个测试团队中推广和使用自动化测试技术,降低自动化测试实施的技术门槛。
②使用外部测试数据文件,使用Excel管理测试用例的集合和每个测试用例的所有执行步骤,实现在一个文件中完成测试用例的维护工作。
③通过定义关键字、操作元素的定位方式和定位表达式和操作值,就可以实现每个测试步骤的执行,可以更加灵活地实现自动化测试的需求。
 ④基于关键字的方式,可以进行任意关键字的扩展,以满足更加复杂的自动化测试需求。
⑤实现定位表达式和测试代码的分离,实现定位表达式直接在数据文件中进行维护。
 ⑥框架提供日志功能,方便调试和监控自动化测试程序的执行。
3、框架代码实现
action 包
action 包为框架第二层“服务层”,相当于对测试对象的一个业务封装。对于接口测试,是对远程方法的一个实现;对于 UI 测试,是对页面元素或操作的一个封装。
page_action.py
 本模块基于关键字格式,封装了页面操作的常用函数,如打开 APP、点击、输入文本等。
import traceback
import os
import time
from appium import webdriver
from util.get_desired_caps import get_desired_caps
from util.datetime_util import *
from util.find_element_util import *
from util.global_var import *
from util.log_util import *
DRIVER = ""
# 打开APP,获取webdriver对象
def open_app():
    global DRIVER
    desired_caps = get_desired_caps()
    DRIVER = webdriver.Remote(APPIUM_SERVER, desired_caps)
# 设定开始活动页
def open_start_activity(app_name, start_activity_name):
    global DRIVER
    DRIVER.start_activity(app_name, start_activity_name)
# 退出APP
def quit_app():
    global DRIVER
    DRIVER.quit()
# 在页面输入框中输入数据
def input_string(location_type, locator_expression, input_content):
    global DRIVER
    find_element(DRIVER, location_type, locator_expression).send_keys(input_content)
# 清除输入框默认内容
def clear(location_type, locator_expression):
    global DRIVER
    find_element(DRIVER, location_type, locator_expression).clear()
# 点击页面元素
def click(location_type, locator_expression):
    global DRIVER
    find_element(DRIVER, location_type, locator_expression).click()
# 断言界面源码是否存在某关键字或关键字符串
def assert_string_in_pagesource(assert_string):
    global DRIVER
    try:
        assert assert_string in DRIVER.page_source, "%s not found in page source!" % assert_string
        info("断言成功【关键字:{}】".format(assert_string))
    except:
        error("断言失败【关键字:{}】".format(assert_string))
        raise
# 强制等待
def sleep(sleep_seconds):
    time.sleep(int(sleep_seconds))
# 批量断言
def assert_app_list(location_type, locator_expression, assert_string):
    global DRIVER
    assert_string_list = assert_string.split(",")
    elements = find_element(DRIVER, location_type, locator_expression)
    for element in elements[:3]:
        assert element.text in assert_string_list
# 截图函数
def take_screenshot():
    global DRIVER
    # 创建当前日期目录
    dir = os.path.join(EXCEPION_PIC_PATH, get_chinese_date())
    if not os.path.exists(dir):
        os.makedirs(dir)
    # 以当前时间为文件名
    file_name = get_chinese_time()
    file_path = os.path.join(dir, file_name+".png")
    try:
        DRIVER.get_screenshot_as_file(file_path)
        # 返回截图文件的绝对路径
        return file_path
    except:
        print("截图发生异常【{}】".format(file_path))
        traceback.print_exc()
        return file_path
business_process 包
business_process 包是框架第三层“测试用例逻辑层”,该层主要是将服务层封装好的各个业务对象,组织成测试逻辑,进行校验。
case_process.py
 测试用例文件的一行数据,拼接其中的操作动作、操作对象、操作值等关键字,形成与 page_action.py 中的函数相对应的字符串,并通过 eval() 转成表达式以执行用例。
 记录该用例的测试结果,如测试执行结果、测试执行时间等。
如需数据驱动的用例集,则获取数据驱动的数据源集合,循环将每组数据传递给用例步骤。
 如果遇到需要参数化的值 ${变量名},则根据数据驱动的数据源,根据变量名进行参数化。
import traceback
import re
from util.global_var import *
from util.log_util import *
from util.datetime_util import *
from util.excel_util import Excel
from action.page_action import *
# 执行单条测试用例(对应excel测试数据文件中的一行测试用例数据)
def execute_case(excel_file_path, case_data, test_data_source=None):
    # 用例数据格式校验
    if not isinstance(case_data, (list, tuple)):
        error("测试用例数据格式有误!测试数据应为列表或元组类型!【%s】" % case_data)
        case_data[CASESTEP_EXCEPTION_INFO_COL_NO] = "测试用例数据格式有误!应为列表或元组类型!【%s】" % case_data
        case_data[CASESTEP_TEST_RESULT_COL_NO] = "Fail"
    # 该用例无需执行
    if case_data[CASESTEP_IS_EXECUTE_COL_NO].lower() == "n":
        info("测试用例步骤【%s】无需执行" % case_data[CASESTEP_NAME_COL_NO])
        return
    # excel对象初始化
    if isinstance(excel_file_path, Excel):
        excel = excel_file_path
    else:
        excel = Excel(excel_file_path)
    # 获取各关键字
    operation_action = case_data[CASESTEP_ACTION_COL_NO]  # 操作动作(即函数名)
    locate_method = case_data[CASESTEP_LOCATE_METHOD_COL_NO]  # 定位方式
    locate_expression = case_data[CASESTEP_LOCATE_EXPRESSION_COL_NO]  # 定位表达式
    operation_value = case_data[CASESTEP_OPERATION_VALUE_COL_NO]  # 操作值
    # 由于数据驱动,需要进行参数化的值
    if test_data_source:
        if re.search(r"\$\{\w+\}", str(operation_value)):
            # 取出需要参数化的值
            key = re.search(r"\$\{(\w+)\}", str(operation_value)).group(1)
            operation_value = re.sub(r"\$\{\w+\}", test_data_source[key], str(operation_value))
            # 将参数化后的值回写excel测试结果中,便于回溯
            case_data[CASESTEP_OPERATION_VALUE_COL_NO] = operation_value
    # 拼接关键字函数
    if locate_method and locate_expression:
        if operation_value:
            func = "%s('%s', '%s', '%s')" % (operation_action, locate_method, locate_expression, operation_value)
        else:
            func = "%s('%s', '%s')" % (operation_action, locate_method, locate_expression)
    else:
        if operation_value:
            func = "%s('%s')" % (operation_action, operation_value)
        else:
            func = "%s()" % operation_action
    # 执行用例
    try:
        eval(func)
        info("测试用例步骤执行成功:【{}】 {}".format(case_data[CASESTEP_NAME_COL_NO], func))
        case_data[CASESTEP_TEST_RESULT_COL_NO] = "Pass"
    except:
        info("测试用例步骤执行失败:【{}】 {}".format(case_data[CASESTEP_NAME_COL_NO], func))
        case_data[CASESTEP_TEST_RESULT_COL_NO] = "Fail"
        error(traceback.format_exc())
        # 进行截图
        case_data[CASESTEP_EXCEPTION_PIC_DIR_COL_NO] = take_screenshot()
        # 异常信息记录
        case_data[CASESTEP_EXCEPTION_INFO_COL_NO] = traceback.format_exc()
    # 测试时间记录
    case_data[CASESTEP_TEST_TIME_COL_NO] = get_english_datetime()
    return case_data
if __name__ == "__main__":
    excel = Excel(TEST_DATA_FILE_PATH)
    excel.get_sheet("登录")
    all_data = excel.get_all_row_data()
    for data in all_data[1:]:
        execute_case(excel, data)
data_source_process.py:
 本模块实现了获取数据驱动所需的数据源集合。
根据数据源 sheet 名,获取该 sheet 所有行数据,每行数据作为一组测试数据。
 每行数据作为一个字典,存储在一个列表中。如 [{“登录用户名”: “xxx”, “登录密码”: “xxx”, …}, {…}, …]
from util.excel_util import Excel
from util.global_var import *
from util.log_util import *
# 数据驱动
# 每行数据作为一个字典,存储在一个列表中。如[{"登录用户名": "xxx", "登录密码": "xxx", ...}, {...}, ...]
def get_test_data(excel_file_path, sheet_name):
    # excel对象初始化
    if isinstance(excel_file_path, Excel):
        excel = excel_file_path
    else:
        excel = Excel(excel_file_path)
    # 校验sheet名
    if not excel.get_sheet(sheet_name):
        error("sheet【】不存在,停止执行!" % sheet_name)
        return
    result_list = []
    all_row_data = excel.get_all_row_data()
    if len(all_row_data) <= 1:
        error("sheet【】数据不大于1行,停止执行!" % sheet_name)
        return
    # 将参数化的测试数据存入全局字典
    head_line_data = all_row_data[0]
    for data in all_row_data[1:]:
        if data[-1].lower() == "n":
            continue
        row_dict = {}
        for i in range(len(data[:-1])):
            row_dict[head_line_data[i]] = data[i]
        result_list.append(row_dict)
    return result_list
if __name__ == "__main__":
    from util.global_var import *
    print(get_test_data(TEST_DATA_FILE_PATH, "搜索词"))
    # [{'搜索词': 'python', '断言词': 'python'}, {'搜索词': 'mysql', '断言词': 'mysql5.6'}]
main_process.py:
 本模块基于 case_process.py 和 data_source_process.py,实现关键字驱动+数据驱动的测试用例集的执行。
suite_process():执行具体的测试用例步骤 sheet(如“登录” sheet、“搜索” sheet 等)
 main_suite_process():执行“测试用例”主 sheet 的用例集。每行用例集对应一个用例步骤 sheet 和数据源 sheet。
from util.excel_util import *
from util.datetime_util import *
from util.log_util import *
from util.global_var import *
from bussiness_process.case_process import execute_case
from bussiness_process.data_source_process import get_test_data
# 执行具体的测试用例步骤sheet
def suite_process(excel_file_path, sheet_name, test_data_source=None):
    """
    :param excel_file_path: excel文件绝对路径或excel对象
    :param sheet_name: 测试步骤sheet名
    :param test_data_source: 数据驱动的数据源,默认没有
    :return:
    """
    # 记录测试结果统计
    global TOTAL_CASE
    global PASS_CASE
    global FAIL_CASE
    # 整个用例sheet的测试结果,默认为全部通过
    suite_test_result = True
    # excel对象初始化
    if isinstance(excel_file_path, Excel):
        excel = excel_file_path
    else:
        excel = Excel(excel_file_path)
    if not excel.get_sheet(sheet_name):
        error("sheet【】不存在,停止执行!" % sheet_name)
        return
    # 获取测试用例sheet的全部行数据
    all_row_data = excel.get_all_row_data()
    if len(all_row_data) <= 1:
        error("sheet【】数据不大于1行,停止执行!" % sheet_name)
        return
    # 标题行数据
    head_line_data = all_row_data[0]
    # 切换到测试结果明细sheet,准备写入测试结果
    if not excel.get_sheet("测试结果明细"):
        error("【测试结果明细】sheet不存在,停止执行!")
        return
    excel.write_row_data(head_line_data, None, True, "green")
    # 执行每行的测试用例
    for row_data in all_row_data[1:]:
        result_data = execute_case(excel, row_data, test_data_source)
        # 无需执行的测试步骤,跳过
        if result_data is None:
            continue
        TOTAL_CASE += 1
        if result_data[CASESTEP_TEST_RESULT_COL_NO].lower() == "fail":
            suite_test_result = False
            FAIL_CASE += 1
        else:
            PASS_CASE += 1
        excel.write_row_data(result_data)
    # 切换到测试结果统计sheet,写入统计数据
    if not excel.get_sheet("测试结果统计"):
        error("【测试结果统计】sheet不存在,停止执行!")
        return
    excel.insert_row_data(1, [TOTAL_CASE, PASS_CASE, FAIL_CASE])
    return excel, suite_test_result
# 执行【测试用例】主sheet的用例集
def main_suite_process(excel_file_path, sheet_name):
    # 初始化excel对象
    excel = Excel(excel_file_path)
    if not excel:
        error("excel数据文件【%s】不存在!" % excel_file_path)
        return
    if not excel.get_sheet(sheet_name):
        error("sheet名称【%s】不存在!" % sheet_name)
        return
    # 获取所有行数据
    all_row_datas = excel.get_all_row_data()
    if len(all_row_datas) <= 1:
        error("sheet【%s】数据不大于1行,停止执行!" % sheet_name)
        return
    # 标题行数据
    head_line_data = all_row_datas[0]
    for row_data in all_row_datas[1:]:
        # 跳过不需要执行的测试用例集
        if row_data[TESTCASE_IS_EXECUTE_COL_NO].lower() == "n":
            info("#" * 50 + " 测试用例集【%s】无需执行!" % row_data[TESTCASE_CASE_NAME_COL_NO] + "#" * 50 + "\n")
            continue
        # 记录本用例集的测试时间
        row_data[TESTCASE_TEST_TIME_COL_NO] = get_english_datetime()
        # 校验用例步骤sheet名是否存在
        if row_data[TESTCASE_CASE_STEP_SHEET_NAME_COL_NO] not in excel.get_all_sheet():
            error("#" * 50 + " 用例步骤集【%s】不存在! " % row_data[TESTCASE_CASE_STEP_SHEET_NAME_COL_NO] + "#" * 50 + "\n")
            row_data[TESTCASE_TEST_RESULT_COL_NO] = "Fail"
            excel.write_row_data(head_line_data, None, True, "red")
            excel.write_row_data(row_data)
            continue
        # 判断本测试用例集是否进行数据驱动
        if row_data[TESTCASE_DATA_SOURCE_SHEET_NAME_COL_NO]:
            # 校验测试数据集sheet名是否存在
            if row_data[TESTCASE_DATA_SOURCE_SHEET_NAME_COL_NO] not in excel.get_all_sheet():
                error("#" * 50 + " 测试数据集【%s】不存在! " % row_data[TESTCASE_DATA_SOURCE_SHEET_NAME_COL_NO] + "#" * 50 + "\n")
                row_data[TESTCASE_TEST_RESULT_COL_NO] = "Fail"
                excel.write_row_data(head_line_data, None, True, "red")
                excel.write_row_data(row_data)
                continue
            # 获取测试数据集
            test_data_source = get_test_data(excel, row_data[TESTCASE_DATA_SOURCE_SHEET_NAME_COL_NO])
            # 每条数据进行一次本用例集的测试
            for data_source in test_data_source:
                info("-" * 50 + " 测试用例集【%s】开始执行!" % row_data[TESTCASE_CASE_NAME_COL_NO] + "-" * 50)
                excel, test_result_flag = suite_process(excel, row_data[TESTCASE_CASE_STEP_SHEET_NAME_COL_NO], data_source)
                # 记录本用例集的测试结果
                if test_result_flag:
                    info("#" * 50 + " 测试用例集【%s】执行成功! " % row_data[TESTCASE_CASE_NAME_COL_NO] + "#" * 50 + "\n")
                    row_data[TESTCASE_TEST_RESULT_COL_NO] = "Pass"
                else:
                    error("#" * 50 + " 测试用例集【%s】执行失败! " % row_data[TESTCASE_CASE_NAME_COL_NO] + "#" * 50 + "\n")
                    row_data[TESTCASE_TEST_RESULT_COL_NO] = "Fail"
                # 全部测试步骤结果写入后,最后写入本用例集的标题行和测试结果行数据
                # 切换到“测试结果明细”sheet,以写入测试执行结果
                excel.get_sheet("测试结果明细")
                excel.write_row_data(head_line_data, None, True, "red")
                excel.write_row_data(row_data)
        # 本用例集无需数据驱动
        else:
            info("-" * 50 + " 测试用例集【%s】开始执行!" % row_data[TESTCASE_CASE_NAME_COL_NO] + "-" * 50)
            excel, test_result_flag = suite_process(excel, row_data[TESTCASE_CASE_STEP_SHEET_NAME_COL_NO])
            # 记录本用例集的测试结果
            if test_result_flag:
                info("#" * 50 + " 测试用例集【%s】执行成功! " % row_data[TESTCASE_CASE_NAME_COL_NO] + "#" * 50 + "\n")
                row_data[TESTCASE_TEST_RESULT_COL_NO] = "Pass"
            else:
                error("#" * 50 + " 测试用例集【%s】执行失败! " % row_data[TESTCASE_CASE_NAME_COL_NO] + "#" * 50 + "\n")
                row_data[TESTCASE_TEST_RESULT_COL_NO] = "Fail"
            # 全部测试步骤结果写入后,最后写入本用例集的标题行和测试结果行数据
            # 切换到“测试结果明细”sheet,以写入测试执行结果
            excel.get_sheet("测试结果明细")
            excel.write_row_data(head_line_data, None, True, "red")
            excel.write_row_data(row_data)
    return excel
if __name__ == "__main__":
    from util.report_util import create_excel_report_and_send_email
    # excel, _ = suite_process(TEST_DATA_FILE_PATH, "进入主页")
    # excel, _ = suite_process(excel, "登录")
    excel = main_suite_process(TEST_DATA_FILE_PATH, "测试用例")
    create_excel_report_and_send_email(excel, "xxxxxxx@qq.com", "app自动化测试", "请查收附件:app自动化测试报告")
| 下面是我整理的2023年最全的软件测试工程师学习知识架构体系图 | 
一、Python编程入门到精通

二、接口自动化项目实战

三、Web自动化项目实战

四、App自动化项目实战

五、一线大厂简历

六、测试开发DevOps体系

七、常用自动化测试工具

八、JMeter性能测试

九、总结(尾部小惊喜)
奋斗是燃烧心灵的力量,是追寻梦想的征程。即使困顿,也要坚守初心,勇往直前。每一次努力都是成长的催化剂,每一次奋斗都将绽放出灿烂的人生之花。
站在人生的起点,勇敢追逐梦想。不畏困难,不惧失败,执着奋斗的脚步踏遍千山万水。坚持信念,拼尽全力,终将在辉煌与成功的殿堂上书写属于自己的传世之作!
不忘初心,坚守梦想之航。即使身陷逆境,也要勇往直前,努力奋斗。相信自己的能力,拥抱挑战的机会,用汗水浇灌成就。奋斗不止于一时,而是人生的永恒追求!



















