Python测试与调试深度解析(unittest/pytest/mock/logging日志/pdb调试·面试真题)

Python 工程闭环不只是写功能,还要通过测试、日志和调试工具保证代码可验证、可定位、可维护。

开发的核心闭环是编码-测试-调试-优化,单纯实现功能只是基础,保证代码稳定、可复用、易排错才是工程化核心。Python拥有完整的测试调试体系:内置标准测试框架、第三方高效测试工具、日志持久化记录、交互式断点调试,覆盖单元测试、接口测试、异常排查、线上问题定位全场景。

本文全覆盖指定重难点:内置unittest框架、主流pytest框架、mock数据模拟、logging分级日志系统、pdb交互式调试工具。全程搭配可落地实战代码、工程规范、高频坑点、面试真题,一站式掌握Python工程化测试调试全套能力。

一、Python测试调试体系认知

1.1 核心工具分工

1、unittest:Python内置标准单元测试框架,无需安装,语法严谨,适配基础单元测试;

2、pytest:第三方主流测试框架,语法简洁、扩展性强,兼容unittest,企业项目首选;

3、mock:测试数据模拟工具,拦截外部依赖,解决接口、数据库、第三方服务依赖测试问题;

4、logging:分级日志记录工具,替代print,实现日志持久化、分级输出、线上问题追溯;

5、pdb:内置交互式断点调试工具,无需IDE,命令行精准排错,适配服务器调试场景。

1.2 工程核心价值

1、单元测试保障代码迭代不翻车,规避改代码出Bug;

2、Mock解耦外部依赖,实现本地独立测试;

3、日志永久记录运行状态,解决线上无断点无法排查问题;

4、pdb精准定位报错堆栈,快速修复代码异常。

二、unittest 内置单元测试框架

unittest是Python官方内置测试框架,基于面向对象设计,拥有固定测试用例格式、断言方法、前置后置钩子,兼容性最强,是所有Python测试的基础。

2.1 核心规则规范

1、测试类必须继承 unittest.TestCase

2、测试方法必须以 test_ 开头,否则不被识别执行;

3、内置丰富断言方法,替代原生 == 判断,精准校验结果;

4、支持setUp/tearDown钩子,实现用例前置初始化、后置清理。

2.2 完整实战案例

import unittest

# 待测试业务函数
def add(a, b):
    return a + b

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("除数不能为0")
    return a / b

# 测试类
class TestCalc(unittest.TestCase):
    # 每个用例执行前自动执行
    def setUp(self):
        print("===== 测试用例开始 =====")

    # 测试加法
    def test_add(self):
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(-1, 1), 0)

    # 测试除法正常逻辑
    def test_divide_normal(self):
        self.assertEqual(divide(10, 2), 5)

    # 测试除法异常逻辑
    def test_divide_error(self):
        with self.assertRaises(ZeroDivisionError):
            divide(5, 0)

    # 每个用例执行后自动执行
    def tearDown(self):
        print("===== 测试用例结束 =====\n")

if __name__ == "__main__":
    unittest.main()

2.3 常用核心断言

self.assertEqual(a, b)    # 相等断言
self.assertNotEqual(a, b) # 不相等断言
self.assertTrue(condition)# 条件为真
self.assertFalse(condition)# 条件为假
self.assertIn(item, iter) # 包含判断
self.assertIsNone(obj)   # 为空判断
self.assertRaises(异常, 函数, 参数) # 异常断言

2.4 全局前置后置钩子

setUpClass/tearDownClass 全局钩子,整个测试类执行前后只运行一次,适合数据库连接、文件初始化等全局操作。

@classmethod
def setUpClass(cls):
    print("测试类全局前置:初始化数据库连接")

@classmethod
def tearDownClass(cls):
    print("测试类全局后置:关闭数据库连接")

三、pytest 企业级主流测试框架

pytest是目前Python项目首选测试框架,兼容unittest用例,语法极简、无需强制写类、支持参数化、插件丰富、报告美观,大幅提升测试效率。

3.1 环境安装与执行命令

# 安装
# pip install pytest

# 常用执行命令
# pytest 文件名.py -v  详细输出
# pytest 文件名.py -k "关键词" 筛选用例
# pytest 文件名.py -s 展示打印日志

3.2 极简测试写法(无需类、无需继承)

pytest打破unittest繁琐规范,普通函数即可作为测试用例,代码极度简洁。

# test_calc.py
def add(a, b):
    return a + b

# 测试函数:test_开头即可
def test_add():
    assert add(1, 2) == 3
    assert add(-2, 5) == 3

3.3 核心高阶功能:参数化测试

无需重复编写相似用例,一组代码批量测试多组参数,工程高频用法。

import pytest

def add(a, b):
    return a + b

# 多组参数批量测试
@pytest.mark.parametrize("a,b,res", [(1,2,3), (0,0,0), (-1,1,0), (10,20,30)])
def test_add_param(a, b, res):
    assert add(a, b) == res

3.4 pytest 夹具(替代setUp)

夹具(fixture)实现灵活的前置后置依赖注入,比unittest钩子更灵活、可复用、可传参。

import pytest

# 定义测试夹具
@pytest.fixture
def init_data():
    print("测试前置:初始化测试数据")
    data = {"name": "测试用户", "age": 20}
    yield data  # 返回数据给用例
    print("测试后置:清理测试数据")

# 用例直接使用夹具
def test_user_data(init_data):
    assert init_data["age"] == 20

3.5 pytest vs unittest 选型总结

1、简单脚本、轻量化测试:任选;

2、企业项目、批量测试、复杂场景:优先 pytest

3、老旧兼容项目:保留unittest,pytest可直接兼容运行。

四、mock 测试依赖模拟(解耦测试核心)

单元测试核心原则:不依赖外部环境。第三方接口、数据库、Redis、支付服务等外部依赖不稳定、不可控,通过mock可以模拟返回数据、拦截真实请求、伪造异常,实现纯本地独立测试。

4.1 mock核心作用

1、模拟第三方接口返回,无需调用真实接口;

2、伪造异常场景,测试代码容错能力;

3、模拟数据库数据,脱离数据库环境测试;

4、统计函数调用次数、传参是否正确。

4.2 基础mock实战

from unittest.mock import Mock

# 1. 创建mock对象
mock_func = Mock()

# 2. 自定义返回值
mock_func.return_value = {"code": 200, "data": "模拟接口数据"}

# 3. 调用mock函数
res = mock_func()
print(res)

# 4. 断言函数被调用
mock_func.assert_called()
# 断言调用次数
print(mock_func.call_count)

4.3 真实场景:mock第三方接口

from unittest.mock import patch
import requests

# 待测试函数(依赖外部接口)
def get_weather():
    # 真实外部接口
    url = "https://xxx.weather.com/api"
    resp = requests.get(url)
    return resp.json()["temp"]

# mock拦截requests.get,不发起真实请求
@patch("requests.get")
def test_get_weather(mock_get):
    # 模拟返回数据
    mock_get.return_value.json.return_value = {"temp": 25}
    temp = get_weather()
    assert temp == 25
    mock_get.assert_called_once()

4.4 伪造异常场景测试

from unittest.mock import patch
import requests

@patch("requests.get")
def test_request_error(mock_get):
    # 模拟接口请求异常
    mock_get.side_effect = Exception("网络请求失败")
    try:
        requests.get("https://www.baidu.com")
    except Exception as e:
        assert "网络请求失败" in str(e)

五、logging 分级日志系统(线上排错必备)

print仅适合临时调试,线上项目禁止使用print。logging是Python标准日志库,支持分级输出、持久化写入、时间戳、行号、日志分类,是线上问题追溯的唯一可靠方式。

5.1 日志五大级别(优先级递增)

1、DEBUG:调试信息,开发阶段使用;

2、INFO:正常运行信息,记录业务流程;

3、WARNING:警告信息,不影响程序运行;

4、ERROR:错误信息,单个功能异常;

5、CRITICAL:严重错误,程序崩溃退出。

5.2 工程标准日志配置(控制台+文件双输出)

import logging

# 全局日志配置
logging.basicConfig(
    level=logging.INFO,  # 日志级别
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",  # 日志格式
    datefmt="%Y-%m-%d %H:%M:%S",
    filename="app.log",  # 日志文件持久化
    filemode="a"  # 追加写入
)

# 获取日志对象
logger = logging.getLogger(__name__)

# 分级输出日志
logger.debug("这是调试日志,默认不输出")
logger.info("程序正常启动")
logger.warning("参数存在默认值,请注意校验")
logger.error("数据解析失败")
logger.critical("程序核心异常,即将退出")

5.3 日志核心优势vs print

1、可分级控制,线上关闭DEBUG日志,减少日志冗余;

2、自动记录时间、代码行号、报错级别,精准定位问题;

3、支持持久化文件存储,重启程序不丢失日志;

4、支持日志切割、分级存储、过滤输出,适配大型项目。

六、pdb 交互式命令行调试(无IDE排错神器)

pdb是Python内置命令行断点调试工具,无需Pycharm、VSCode等IDE,可在服务器、纯命令行环境下打断点、查看变量、修改数值、逐行执行,是线上服务器排错的核心技能。

6.1 快速插入断点

def calc(a, b):
    import pdb;pdb.set_trace()  # 插入断点
    res = a ** 2 + b ** 2
    return res

print(calc(3, 4))

6.2 pdb核心常用指令

1、p 变量名:打印查看变量值;

2、n:下一步(跳过函数内部);

3、s:单步进入(进入函数内部);

4、c:继续执行,跳出当前断点;

5、q:退出调试;

6、l:查看当前代码上下文。

6.3 新版breakpoint(推荐替代pdb)

Python3.7+ 内置 breakpoint() 函数,直接替代import pdb,语法更简洁。

def calc(a, b):
    breakpoint()  # 极简断点
    res = a ** 2 + b ** 2
    return res

七、工程高频坑点汇总

1、unittest用例不执行:方法未以test_开头、未继承TestCase;

2、mock失效:patch装饰器路径错误,必须mock「被调用处」的模块;

3、日志无输出:日志级别设置过高,低级别日志被过滤;

4、print滥用:线上项目无日志持久化,报错无法追溯历史运行状态;

5、pdb断点残留:上线代码忘记删除断点,导致程序阻塞等待输入。

八、高频面试真题汇总

题目1:unittest和pytest的区别?项目如何选型?

答案:unittest是内置框架,语法严谨、面向对象,规范繁琐;pytest是主流第三方框架,语法简洁、兼容unittest、支持参数化和夹具,扩展性更强。企业项目优先pytest,简单轻量化测试、兼容老旧代码可使用unittest。

题目2:mock的作用是什么?为什么单元测试需要mock?

答案:mock用于模拟外部依赖,拦截真实接口、数据库、第三方服务请求。单元测试要求独立无依赖,外部环境不稳定、不可控,通过mock可以伪造数据和异常,实现本地独立测试,保证用例稳定可复现。

题目3:logging相比print的优势?

答案:print仅临时打印,无级别、无时间、无法持久化;logging支持五级日志分级、自动记录时间代码行号、文件持久化存储、日志过滤切割,适配线上项目问题追溯,是工程化唯一规范日志方案。

题目4:pdb调试的使用场景?常用指令有哪些?

答案:pdb用于无IDE的服务器环境交互式断点调试,可逐行执行、查看修改变量。常用指令:p打印变量、n下一步、s单步进入、c继续执行、q退出调试。

题目5:pytest夹具fixture的作用?和setUp的区别?

答案:fixture是pytest的依赖前置后置工具,支持灵活传参、作用域配置、数据复用,比unittest固定的setUp/tearDown更灵活,可精准为指定用例提供依赖,粒度更细、复用性更强。

九、全文终极背诵总结

1、unittest:官方内置测试框架,固定类和方法规范,适合基础标准化单元测试。

2、pytest:企业主流框架,语法极简、支持参数化、夹具,兼容unittest,高效适配批量测试。

3、mock模拟:解耦外部依赖,伪造数据与异常,实现纯本地、稳定可复现的单元测试。

4、logging日志:分级、持久化、可追溯,替代print,是线上问题排查的核心依据。

5、pdb调试:命令行交互式断点调试,适配服务器无IDE场景,快速定位代码异常。

6、工程规范:开发必写单元测试、线上只用logging、依赖测试必用mock、服务器排错用pdb。

后浪云移动端信息流广告 后浪云主机服务 适合长期部署、独立站和海外机房需求。