当前位置:首页>python>Python速成(为自动化测试入门精简)

Python速成(为自动化测试入门精简)

  • 2026-09-03 21:26:09
Python速成(为自动化测试入门精简)

软件测试从入门到精通 · 第29篇

如果你准备踏入自动化测试领域,那么Python是自动化测试领域的主流语言。它语法简洁、生态丰富、学习成本低,Selenium、pytest、requests、Appium等主流测试工具对Python均有良好支持。

但很多Python教程的问题是太全面,一上来就从计算机原理讲到设计模式,新手学了两周还没写到第一个测试脚本。所以,本文只讲测试工作中真正会用到的东西,够用即可,需要时再深入学习。

本文假设你有基本的编程概念(比如知道什么是变量),如果尚未接触也不必担心,每个知识点都会附上简短代码示例。


一、变量与数据类型

Python是动态类型语言,变量不需要声明类型,直接赋值即可。

1.1 字符串(str)

字符串是测试中常用的数据类型,URL、页面标题、元素文本、断言信息,都属于字符串类型。Python提供了丰富的字符串操作方法,以下是一些常用的。

# 字符串的四种写法url = "https://www.example.com"       # 双引号title = '登录页面'                      # 单引号(效果一样)html = """<div>    <p>多行文本用三引号</p></div>"""                               # 三引号可以跨行# 常用操作text = "  Hello Selenium  "print(text.strip())          # "Hello Selenium"—去首尾空格print(text.upper())          # "  HELLO SELENIUM  "—全大写print(text.lower())          # "  hello selenium  "—全小写print("Selenium" in text)    # True — 判断是否包含print(text.replace("Selenium", "Playwright"))  # 替换print(text.split())          # ['Hello', 'Selenium']—按空格分割# f-string:测试日志中常用的格式化方式name = "admin"password = "123456"print(f"正在登录,用户名:{name},密码:{password}")# f-string调试语法(Python 3.8+):f"{变量=}"直接打印"变量名=值"print(f"{name=}")       # name='admin'print(f"{password=}")   # password='123456'

1.2 整数与浮点数(int/float)

测试中整数和浮点数主要用于计数、超时设置、价格比较等场景。Python支持类型之间的相互转换,这在解析测试数据时经常用到。

# 整数count = 10retry_times = 3# 浮点数price = 19.99timeout = 30.5# 类型转换(测试中经常用到)num_str = "100"real_num = int(num_str)      # 字符串转整数->100float_num = float("3.14")    # 字符串转浮点数->3.14str_num = str(100)           # 整数转字符串->"100"

1.3 列表(list)

列表是Python较为常用的数据结构,测试数据驱动、元素集合管理都离不开它。下面演示列表的增删改查、切片遍历和推导式等核心操作。

# 创建列表test_cases = ["登录成功", "用户名错误", "密码为空"]numbers = [1, 2, 3, 4, 5]mixed = ["admin", 123, True]     # 可以混放不同类型# 增删改查test_cases.append("验证码错误")    # 末尾追加test_cases.insert(1, "密码错误")   # 指定位置插入test_cases.remove("用户名错误")    # 按值删除deleted = test_cases.pop()       # 删除并返回最后一个test_cases[0] = "登录正常"        # 修改指定位置# 切片:列表[起始:结束:步长]print(test_cases[1:3])           # 第2到第3个元素print(test_cases[::-1])          # 倒序# 遍历列表for case in test_cases:    print(f"执行测试用例:{case}")# 列表推导式:一行代码生成列表squares = [x**2 for x in range(10)]   # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

1.4 字典(dict)

字典是键值对结构,测试中用于存储配置、测试数据、请求参数等。与列表相比,字典通过键名(而不是数字索引)来存取数据,代码可读性更高。

# 创建字典test_data = {    "username": "admin",    "password": "123456",    "expected_title": "管理后台"}# 读取print(test_data["username"])            # adminprint(test_data.get("email", "无"))     # 无。键不存在时返回默认值# 修改与新增test_data["password"] = "new_pass"      # 修改test_data["remember"] = True            # 新增# 遍历for key, value in test_data.items():    print(f"{key}: {value}")# 嵌套结构(测试中常用)test_suite = {    "suite_name": "登录模块",    "cases": [        {"id": "TC001", "title": "正常登录", "data": {"user": "admin", "pwd": "123"}},        {"id": "TC002", "title": "密码错误", "data": {"user": "admin", "pwd": "999"}},    ]}print(test_suite["cases"][0]["data"]["user"])   # admin# 字典合并(Python 3.9+):|运算符,等价于{**a, **b}config_a = {"browser": "Chrome", "timeout": 30}config_b = {"headless": True, "timeout": 15}   # 重复键:后面覆盖前面merged = config_a | config_b # {'browser': 'Chrome', 'timeout': 15, 'headless': True}

1.5 元组(tuple)

元组是不可变的有序序列,创建后不能增、删、改,适合存储不会变化的常量配置。

# 创建元组browser_types = ("Chrome", "Firefox", "Edge")screen_size = (1920, 1080)# 元组拆包width, height = screen_sizeprint(f"宽度:{width},高度:{height}")

为什么用元组?

作为字典的键、函数返回多个值、保护数据不被意外修改。


二、运算符与条件判断

2.1 运算符

Python运算符分为几个大类。算术运算符处理数字计算,比较运算符用于断言判断,逻辑运算符组合多个条件,成员运算符检查元素是否在容器中。这些是编写测试逻辑的基础。

# 算术运算符a, b = 10, 3print(a + b)    # 13  加print(a - b)    # 7   减print(a * b)    # 30  乘print(a / b)    # 3.333...  除(浮点数)print(a // b)   # 3   整除print(a % b)    # 1   取余print(a ** b)   # 1000  幂运算# 比较运算符。测试断言的基础print(5 == 5)   # True  等于print(5 != 3)   # True  不等于print(5 > 3)    # True  大于print(5 < 3)    # False 小于print(5 >= 5)   # True  大于等于print(5 <= 3)   # False 小于等于# 逻辑运算符。组合多个条件logged_in = Trueis_admin = Falseprint(logged_in and is_admin)    # False 且print(logged_in or is_admin)     # True  或print(not logged_in)             # False 非# 成员运算符。判断元素是否在容器中print("admin" in ["admin", "guest"])        # Trueprint("error" not in "success message")     # True

2.2 条件判断(if-elif-else)

条件判断是编写测试逻辑的核心,例如根据执行结果进行不同的断言。

# 基本结构status_code = 200if status_code == 200:    print("请求成功")elif status_code == 404:    print("页面不存在")elif status_code == 500:    print("服务器内部错误")else:    print(f"未知状态码:{status_code}")# 三元表达式(一行if-else)result = "通过" if status_code == 200 else "失败"# 实战:根据页面元素判断登录结果def check_login(page_source):    if "欢迎回来" in page_source:        return "登录成功"    elif "用户名或密码错误" in page_source:        return "账号密码错误"    elif "验证码错误" in page_source:        return "验证码错误"    else:        return "未知错误"# 嵌套条件response_text = '{"code": 200, "message": "success"}'   # 模拟接口响应内容(JSON格式字符串)if status_code == 200:    if "success" in response_text:        print("操作成功")    else:        print("返回成功但操作未生效")

三、循环

3.1 for循环

for循环用于遍历可迭代对象,在测试中常用于遍历测试数据、元素列表等。除了基本的列表遍历,Python还提供了range()、enumerate()、zip()等辅助函数,让循环更加灵活。

# 遍历列表elements = ["用户名输入框", "密码输入框", "登录按钮"]for element in elements:    print(f"正在定位:{element}")# 遍历字典config = {"browser": "Chrome", "timeout": 30, "headless": True}for key, value in config.items():    print(f"{key} = {value}")# range()函数for i in range(5):              # 0, 1, 2, 3, 4    print(f"第 {i+1} 次重试")for i in range(2, 6):           # 2, 3, 4, 5    print(i)for i in range(0, 10, 2):       # 0, 2, 4, 6, 8(步长2)    print(i)# enumerate():同时获取索引和值test_cases = ["登录", "注册", "找回密码"]for index, case in enumerate(test_cases):    print(f"用例 {index+1}:{case}")# zip():同时遍历多个列表urls = ["/login", "/register", "/forgot"]titles = ["登录页面", "注册页面", "找回密码"]for url, title in zip(urls, titles):    print(f"打开 {url},预期标题:{title}")# break与continuefor case in test_cases:    if case == "注册":        continue          # 跳过注册用例    if case == "找回密码":        break              # 到这就停止    print(f"执行:{case}")

3.2 while循环

while循环在测试中常用于等待、重试等需要满足某个条件才停止的场景。与for循环不同,while不依赖可迭代对象,只依赖条件表达式。

# 基本while循环count = 0while count < 3:    print(f"第 {count+1} 次重试")    count += 1# 实战:重试机制import timedef wait_for_element(driver, locator, timeout=10):    """等待元素出现,最多等待timeout秒"""    start_time = time.time()    while time.time() - start_time < timeout:        try:            element = driver.find_element(*locator)            return element        except Exception:            time.sleep(0.5)    raise TimeoutError(f"等待超时:{locator}")

四、函数

函数是代码复用的基本单位。测试框架中,测试用例本身就是函数。

4.1 基本定义与调用

函数通过def关键字定义。参数可以设置默认值,调用时可以选择按位置传参或按关键字传参,非常灵活。

# 知识点1:用def定义函数。def函数名(参数): 函数体def greet():    print("Hello, Tester!")greet()                               # 知识点2:调用函数。函数名(),无参数就写空括号# 知识点3:有参函数+return。函数用return把结果交还给调用处def login(username, password):    print(f"用户名:{username},密码:{password}")    return f"{username} 登录成功"result = login("admin", "123456")     # 知识点4:位置传参。按定义顺序依次传入print(result)                         # result接住了return返回的值# 知识点5:默认参数。定义时给参数赋默认值,调用时可以省略def open_browser(browser="Chrome", headless=False):    print(f"启动 {browser} 浏览器,无头模式:{headless}")open_browser()                          # 知识点6:不传参→全部用默认值(Chrome, False)open_browser("Firefox", True)          # 知识点7:位置传参→按顺序覆盖(Firefox, True)open_browser(headless=True)            # 知识点8:关键字传参→"参数名=值",可只传部分、顺序随意# 知识点9:类型注解(Python 3.5+,3.9+支持内置泛型list[str]/dict[str, int])# 现代Python项目的标准做法,标注参数和返回值类型,提升可读性、IDE提示与静态检查def login(username: str, password: str) -> str:    """登录(带类型注解:参数为str,返回str)"""    return f"{username} 登录成功"

4.2 可变参数

当不确定函数需要接收多少个参数时,可以用*args和**kwargs。*args将多个位置参数打包成元组,**kwargs将多个关键字参数打包成字典。

# 知识点1:*args。形参前加一个*,把任意多个位置参数打包成元组def run_cases(*case_names):    for name in case_names:            # case_names是元组 ("TC001","TC002","TC003")        print(f"执行用例:{name}")run_cases("TC001", "TC002", "TC003")   # 传几个都行,都会被收进元组# 知识点2:**kwargs。形参前加两个*,把任意多个关键字参数打包成字典def set_config(**kwargs):    for key, value in kwargs.items():  # kwargs是字典 {"browser":"Chrome", ...}        print(f"配置 {key} = {value}")set_config(browser="Chrome", timeout=30, base_url="https://test.com")

4.3 返回值

函数用return返回结果。Python函数可以返回多个值,实际是打包成一个元组,调用时可以用元组拆包分别接收。

# 知识点1:return返回多个值。实际是打包成一个元组返回def get_bounds(element):    x = element.location['x']    y = element.location['y']    width = element.size['width']    height = element.size['height']    return x, y, width, height         # 等价于return (x, y, width, height)# 模拟一个页面元素(实际使用中是Selenium定位到的WebElement)class MockElement:    location = {'x': 10, 'y': 20}    size = {'width': 800, 'height': 600}some_element = MockElement()# 知识点2:元组拆包。用"变量1, 变量2, ..."按位置接收返回的每个值x, y, w, h = get_bounds(some_element)print(f"元素位置:({x}, {y}),尺寸:{w}x{h}")

4.4 lambda表达式

lambda是匿名函数,适合在需要函数对象但逻辑非常简单时使用。测试中常见于sorted排序的key参数、filter/map等场景。

# 知识点1:lambda语法。lambda 参数: 表达式,返回表达式的结果,适合一行能写下的简单逻辑# 下面的key=lambda x: x["duration"]等价于定义函数def f(x): return x["duration"]test_results = [    {"name": "TC001", "duration": 3.2},    {"name": "TC002", "duration": 1.5},    {"name": "TC003", "duration": 4.8},]# 知识点2:sorted的key参数。指定按什么排序,这里按字典中duration的值升序sorted_results = sorted(test_results, key=lambda x: x["duration"])print(sorted_results)                  # 按耗时排序:TC002(1.5)→TC001(3.2)→TC003(4.8)# 知识点3:filter(函数, 列表)。用函数逐个判断,保留返回True的元素numbers = [1, 2, 3, 4, 5, 6]evens = list(filter(lambda x: x % 2 == 0, numbers))  # 保留偶数->[2, 4, 6]# 知识点4:map(函数, 列表)。用函数逐个转换每个元素doubled = list(map(lambda x: x * 2, numbers))         # 每个数乘2-> [2, 4, 6, 8, 10, 12]

五、类与对象

测试中类常用于封装测试用例、测试数据等对象,比如把一条测试用例抽象成一个类,方便统一管理和复用。不需要深入理解面向对象理论,先掌握类的定义、属性和方法即可。

# 知识点1:用class定义类(类名习惯首字母大写)class TestCase:    """测试用例类"""    # 知识点2:类变量。定义在类里、所有实例共享同一份    category = "功能测试"    # 知识点3:构造方法__init__。创建实例时自动调用,self表示当前这个实例    def __init__(self, case_id, title):        self.case_id = case_id            # 知识点4:实例变量。每个实例各有一份,通过 self 存取        self.title = title        self.status = "未执行"    # 知识点5:方法。类里的函数,第一个参数固定是self,用self.xxx访问实例数据    def run(self):        """执行用例(示例中模拟执行)"""        print(f"正在执行:{self.case_id}{self.title}")        self.status = "通过"              # 在方法内修改实例变量    def report(self):        """返回用例结果"""        return f"[{self.status}] {self.case_id} - {self.title}"# 知识点6:实例化。"类名(参数)"创建对象,参数会传给__init__case1 = TestCase("TC001", "正常登录")case1.run()                              # 知识点7:调用方法。"实例.方法名()"print(case1.report())case2 = TestCase("TC002", "密码错误")     # 每个实例的实例变量互相独立print(case2.report())          # 未执行,状态为"未执行"# 知识点8:继承。"class 子类(父类)",子类自动拥有父类的属性和方法class ApiTestCase(TestCase):    """接口测试用例(继承TestCase)"""    category = "接口测试"                # 知识点9:子类覆盖类变量(重新赋值即可)    # 知识点10:super().__init__(...)。调用父类的构造方法,复用父类初始化逻辑    def __init__(self, case_id, title, method, path):        super().__init__(case_id, title)        self.method = method               # 知识点11:子类新增自己的实例变量        self.path = path    # 知识点12:方法重写。与父类方法同名,子类实例调用时优先用子类的版本    def run(self):        """重写父类方法"""        print(f"发送 {self.method} 请求:{self.path}")        self.status = "通过"api_case = ApiTestCase("TC010", "查询用户列表", "GET", "/api/users")api_case.run()                           # 实际调用的是ApiTestCase自己的run()print(api_case.report())                 # report()未重写,沿用父类的实现print(f"用例分类:{TestCase.category} / {ApiTestCase.category}")  # 类变量可直接用"类名.变量"访问

六、模块与异常处理

6.1 模块导入(import)

Python通过import语句引入其他模块的功能。模块可以是Python标准库(如 time)、第三方库(如selenium)、或你自定义的.py文件。常见的导入方式有以下几种。

# 导入整个模块import timetime.sleep(2)# 导入模块并起别名import selenium.webdriver as wd      # 之后可用wd.Chrome(),效果同from selenium import webdriverfrom selenium.webdriver.common.by import By# 从模块导入特定内容from selenium import webdriverfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC# 导入自定义模块(同目录下的py文件)from login_page import LoginPagefrom test_data import user_data# 按目录导入from pages.login_page import LoginPagefrom utils.config import BASE_URL

6.2 异常处理(try-except)

自动化测试中,网络超时、元素找不到、断言失败都是异常。合理处理异常可以提高脚本的健壮性。Python的异常处理包括try、except、else、finally四个子句,以及自定义异常。

# 知识点1:try-except基本结构。把可能出错的代码放try,一旦出错就跳到except处理try:    price = int("19.99")          # "19.99"不是合法的整数字符串,会抛ValueError    print(f"价格:{price}")except Exception as e:    print(f"转换失败:{e}")        # 捕获任意异常,e是错误信息# 知识点2:捕获特定异常。按异常类型分别处理,except从上往下匹配第一个try:    data = {"username": "admin"}    print(data["password"])       # 键不存在,抛KeyErrorexcept KeyError:    print("缺少键:password")      # 只处理KeyErrorexcept ValueError:    print("值不合法")except Exception as e:    print(f"未预期的错误:{type(e).__name__} - {e}")# 知识点3:else。try没有异常时才执行else里的代码# 知识点4:finally。无论有没有异常都会执行,是清理资源的固定位置try:    result = 10 / 2               # 正常执行,不会触发exceptexcept ZeroDivisionError:    print("除数不能为零")else:    print(f"计算结果:{result}")    # try成功时执行finally:    print("清理资源")              # 无论是否异常都会执行# 知识点5:自定义异常。继承Exception,用于表达业务上的错误情况class TestDataError(Exception):    """测试数据异常"""    passdef validate_test_data(data):    if "username" not in data:        raise TestDataError("缺少必填字段:username")   # raise主动抛出异常validate_test_data({"password": "123"})   # 会抛出TestDataError# 知识点6:安全地取值,键不存在时返回默认值而不是让程序崩溃def safe_get(dict_data, key, default="未找到"):    try:        return dict_data[key]    except KeyError:        return defaultuser = {"username": "admin"}print(safe_get(user, "email"))     # 未找到(键不存在,不崩溃)print(safe_get(user, "username"))  # admin

七、文件与数据格式

测试中经常需要读取测试数据文件或写入测试结果日志。

7.1 文本文件

open()加with语句是Python读写文件的标准方式。with会自动管理文件关闭,即使发生异常也能正确释放资源。

# 知识点1:with open(文件名, 模式, 编码) as f,上下文管理器,用完自动关闭文件,即使出异常也会释放资源# 知识点2:模式"w"写入(会覆盖原文件已有内容,文件不存在则创建)# 写入文件with open("test_result.txt", "w", encoding="utf-8") as f:    f.write("测试报告\n")    f.write("=" * 40 + "\n")    f.write("用例总数:10\n")    f.write("通过:8\n")    f.write("失败:2\n")# 知识点3:模式"a"追加(在文件末尾续写,不清空原内容,文件不存在则创建)# 追加写入with open("test_log.txt", "a", encoding="utf-8") as f:    f.write(f"[2024-01-15 10:30:00] 执行登录用例\n")# 知识点4:模式"r"读取+f.read()一次性读出整个文件内容# 读取整个文件with open("test_result.txt", "r", encoding="utf-8") as f:    content = f.read()    print(content)# 知识点5:逐行读取,for line in f按行遍历,line自带结尾换行符# 知识点6:line.strip()去掉每行首尾空白(换行符等);if case_name跳过空行# 逐行读取with open("test_cases.txt", "r", encoding="utf-8") as f:    for line in f:        case_name = line.strip()        if case_name:              # 跳过空行            print(f"执行:{case_name}")

7.2 文件模式速查表

r:只读,文件不存在时报错。

w:写入(覆盖),文件不存在时创建新文件。

a:追加,文件不存在时创建新文件。

r+:读写,文件不存在时报错。

b:二进制模式(与上述模式组合使用,如'rb'或'wb')。

7.3 JSON处理

JSON是测试中最常见的数据交换格式,API测试的请求和响应基本都是 JSON。Python内置的json模块提供了dumps(字典→JSON字符串)和 loads(JSON字符串→字典)两种核心方法,以及对应的文件读写方法dump和load。

import json# 知识点1:dumps,Python字典->JSON字符串(用于打印、拼接、传输)# 知识点2:ensure_ascii=False让中文原样输出(否则转成\uXXXX);indent=2缩进美化test_data = {    "username": "admin",    "password": "123456",    "remember": True,    "roles": ["admin", "editor"]}json_str = json.dumps(test_data, ensure_ascii=False, indent=2)print(json_str)# 输出:# {#   "username": "admin",#   "password": "123456",#   "remember": true,#   "roles": [#     "admin",#     "editor"#   ]# }# 知识点3:dump,把字典直接写入JSON文件(dumps返回字符串、dump写入文件,注意多一个s的区别)# 写入JSON文件(dump,生成test_data.json)with open("test_data.json", "w", encoding="utf-8") as f:    json.dump(test_data, f, ensure_ascii=False, indent=2)# 知识点4:load,从JSON文件读回字典(dump和load成对,配合文件使用)# 读取JSON文件(load,读回字典)with open("test_data.json", "r", encoding="utf-8") as f:    loaded_data = json.load(f)print(loaded_data["username"])        # "admin"print(loaded_data["roles"])           # ['admin', 'editor']# 知识点5:loads,JSON字符串->Python字典(解析接口响应的标准动作)# JSON字符串->Python字典(loads,用于解析接口响应)response = '{"code": 200, "message": "success", "data": {"token": "abc123"}}'data = json.loads(response)print(data["code"])                  # 200print(data["data"]["token"])        # abc123

7.4 CSV处理

CSV是测试数据驱动的常用格式,可以方便地用Excel编辑。Python的csv模块提供了DictReader(将每行读为字典)和writer(写入行数据)两种核心工具。

import csv# 知识点1:csv.writer+writerows,把列表的列表逐行写入文件# 知识点2:newline=""必须加,Windows下不加会在每行后多出空行# 知识点3:第一行为表头,DictReader靠它识别列名# 写入CSV文件(生成test_users.csv,第一行为表头)users = [    ["username", "password"],     # 表头,DictReader靠它识别列名    ["admin", "123456"],    ["tester", "abc123"],]with open("test_users.csv", "w", encoding="utf-8", newline="") as f:    writer = csv.writer(f)    writer.writerows(users)# 知识点4:csv.DictReader,把每行读成字典,用表头当键名(row['username']这种取法)# 读取CSV文件with open("test_users.csv", "r", encoding="utf-8") as f:    reader = csv.DictReader(f)        # 每行变为字典    for row in reader:        print(f"用户名:{row['username']},密码:{row['password']}")        # 这里执行具体的测试逻辑# 知识点5:数据驱动:遍历行+条件判断+异常处理+字典解包合并def test_login_from_csv(csv_path):    with open(csv_path, "r", encoding="utf-8") as f:        reader = csv.DictReader(f)        results = []        for row in reader:            try:                # 执行登录操作(这里简化演示)                if row["username"] and row["password"]:                    result = "通过"                else:                    result = "失败"            except Exception as e:                result = f"异常:{e}"            results.append({**row, "result": result})   # {**row, ...}把result合并进原字典        return results# 调用函数print(test_login_from_csv("test_users.csv"))

八、包管理与综合实战

8.1 pip安装第三方库

Python的强大在于生态,测试中要用到的库基本都通过pip安装。pip是Python的包管理工具,可以安装、查看、卸载第三方库。推荐将项目依赖写入requirements.txt统一管理。

# 基本安装pip install selenium# 安装指定版本pip install selenium==4.47.0# 批量安装(依赖的包写在requirements.txt)pip install -r requirements.txt# 查看已安装的库pip list# 查看某个库的信息pip show selenium# 卸载pip uninstall selenium

测试工程常用的requirements.txt示例:

selenium>=4.47.0pytest>=9.1.1pytest-html>=4.2.0pytest-xdist>=3.8.0pytest-rerunfailures>=16.5requests>=2.34.2webdriver-manager>=4.1.2openpyxl>=3.1.5PyMySQL>=1.2.0allure-pytest>=2.16.0

8.2 综合实战:一个可运行的登录测试脚本

下面将这些知识点整合起来,编写一个纯Python可运行的测试脚本,综合演示:字典配置、函数封装、CSV数据驱动、循环遍历测试用例、条件判断做断言、异常处理、JSON生成测试报告。脚本不依赖浏览器和网络,被测对象是一个模拟登录服务,把前面学的基础知识串成一条完整的测试流程,复制到本地就能跑起来。

配套两个文件(放在同一目录,运行python test_login.py):

  • test_login.py:测试脚本(下方完整代码)。

  • test_users.csv:测试数据(下方给出内容)。

"""综合实战:一个纯Python可运行的登录测试脚本演示:字典配置、函数封装、CSV数据驱动、循环、条件断言、异常处理、JSON报告运行:python test_login.py(需同目录有test_users.csv)"""import csvimport json# ---------- 1.配置数据(字典) ----------CONFIG = {    "csv_path": "test_users.csv",       # 测试数据文件    "report_path": "test_report.json",  # 报告输出文件}# ---------- 2.被测系统:模拟登录服务 ----------def login(username, password):    """模拟登录接口:admin/123456登录成功,其余返回错误信息"""    if not username or not password:        return {"code": 1001, "message": "用户名或密码不能为空"}    if username == "admin" and password == "123456":        return {"code": 0, "message": "登录成功"}    return {"code": 1002, "message": "用户名或密码错误"}# ---------- 3.测试数据驱动(CSV读取+列表遍历) ----------def load_test_data(csv_path):    """从CSV加载测试数据"""    test_data = []    with open(csv_path, "r", encoding="utf-8-sig") as f:   # utf-8-sig兼容Excel保存的带BOM文件        reader = csv.DictReader(f)        for row in reader:            test_data.append(row)    return test_data# ---------- 4.测试执行引擎(循环+条件断言+异常处理) ----------def run_login_test(test_cases):    """执行登录测试,返回测试结果列表"""    results = []    for case in test_cases:        result = {"case_id": case["id"], "title": case["title"], "status": "未知"}        try:            resp = login(case["username"], case["password"])   # 调用被测系统            expected = case.get("expected", "success")            if expected == "success":                          # 断言:期望登录成功                if resp["code"] == 0:                    result["status"] = "通过"                else:                    result["status"] = "失败"                    result["error"] = f"期望登录成功,实际:{resp['message']}"            elif expected == "fail":                           # 断言:期望登录失败                if resp["code"] != 0:                    result["status"] = "通过"                    result["actual_message"] = resp["message"]                else:                    result["status"] = "失败"                    result["error"] = "期望登录失败,实际却成功了"        except Exception as e:                                 # 异常处理            result["status"] = "异常"            result["error"] = f"{type(e).__name__}: {e}"        results.append(result)    return results# ---------- 5.生成测试报告(JSON写入) ----------def generate_report(results, output_path):    """生成JSON格式的测试报告"""    total = len(results)    passed = sum(1 for r in results if r["status"] == "通过")    failed = sum(1 for r in results if r["status"] == "失败")    error = sum(1 for r in results if r["status"] == "异常")    report = {        "summary": {            "total": total,            "passed": passed,            "failed": failed,            "error": error,            "pass_rate": f"{passed/total*100:.1f}%" if total > 0 else "0%"        },        "details": results    }    with open(output_path, "w", encoding="utf-8") as f:        json.dump(report, f, ensure_ascii=False, indent=2)    print("=" * 50)    print(f"测试完成 | 总计:{total} | 通过:{passed} | 失败:{failed} | 异常:{error}")    print(f"通过率:{report['summary']['pass_rate']}")    print("=" * 50)# ---------- 6.主入口 ----------if __name__ == "__main__":    test_cases = load_test_data(CONFIG["csv_path"])    if not test_cases:        print("没有测试数据,请检查test_users.csv")    else:        results = run_login_test(test_cases)        generate_report(results, CONFIG["report_path"])

test_users.csv 内容(与脚本放在同一目录):

id,title,username,password,expectedTC001,正常登录,admin,123456,successTC002,密码错误,admin,999999,failTC003,空密码,admin,,fail

运行输出(python test_login.py):

==================================================测试完成 | 总计:3 | 通过:3 | 失败:0 | 异常:0通过率:100.0%==================================================

运行后会在同目录生成test_report.json,包含统计摘要和每个用例的详细结果。把这3条用例的expected改成相反值再跑一次,你就能看到失败分支和50%以下的通过率。试着改改数据,理解断言是如何生效的。


写在最后

这篇文章以够用为原则,把测试工作中常用的Python知识点串了一遍。这些不是全部,但足够让你看懂和编写绝大部分自动化测试脚本了。

用一句话总结Python的学习思路:先运行起来,再逐步优化。 不需要先看完一整本教程才开始写测试。把本文的每个代码块复制运行一遍,然后尝试改写参数、修改逻辑,慢慢就熟练了。

下一步,我们将正式进入Selenium WebDriver的世界,用Python驱动浏览器,开始真正意义上的自动化测试。


本文为《软件测试从入门到精通》系列第29篇,本系列持续更新中。

最新文章

随机文章