Python中级教程 第七课:网络编程、并发与项目实践 5/7

浏览量:13 次 发布时间:2026-01-15 18:06 作者:明扬工控商城 下载docx

最近更新:Python中级教程 第三课:面向对象编程深入与装饰器

第五部分:测试与调试技巧

5.1 单元测试

python

# test_example.py


import unittest

import sys

import os


# 要测试的模块

class Calculator:

   """简单的计算器类"""

   

   def __init__(self):

       self.history = []

   

   def add(self, a, b):

       """加法"""

       result = a + b

       self.history.append(f"{a} + {b} = {result}")

       return result

   

   def subtract(self, a, b):

       """减法"""

       result = a - b

       self.history.append(f"{a} - {b} = {result}")

       return result

   

   def multiply(self, a, b):

       """乘法"""

       result = a * b

       self.history.append(f"{a} × {b} = {result}")

       return result

   

   def divide(self, a, b):

       """除法"""

       if b == 0:

           raise ValueError("除数不能为零")

       result = a / b

       self.history.append(f"{a} ÷ {b} = {result}")

       return result

   

   def get_history(self):

       """获取历史记录"""

       return self.history

   

   def clear_history(self):

       """清除历史记录"""

       self.history.clear()


# 测试类

class TestCalculator(unittest.TestCase):

   """Calculator类的测试用例"""

   

   def setUp(self):

       """每个测试方法前执行"""

       self.calc = Calculator()

       print(f"\n开始测试: {self._testMethodName}")

   

   def tearDown(self):

       """每个测试方法后执行"""

       print(f"测试完成: {self._testMethodName}")

   

   # 测试加法

   def test_add_integers(self):

       """测试整数加法"""

       result = self.calc.add(3, 4)

       self.assertEqual(result, 7)

   

   def test_add_floats(self):

       """测试浮点数加法"""

       result = self.calc.add(3.5, 2.1)

       self.assertAlmostEqual(result, 5.6, places=2)

   

   def test_add_negative(self):

       """测试负数加法"""

       result = self.calc.add(-3, 4)

       self.assertEqual(result, 1)

   

   # 测试减法

   def test_subtract_integers(self):

       """测试整数减法"""

       result = self.calc.subtract(10, 4)

       self.assertEqual(result, 6)

   

   def test_subtract_negative_result(self):

       """测试结果为负的减法"""

       result = self.calc.subtract(3, 7)

       self.assertEqual(result, -4)

   

   # 测试乘法

   def test_multiply_integers(self):

       """测试整数乘法"""

       result = self.calc.multiply(3, 4)

       self.assertEqual(result, 12)

   

   def test_multiply_by_zero(self):

       """测试乘以零"""

       result = self.calc.multiply(5, 0)

       self.assertEqual(result, 0)

   

   # 测试除法

   def test_divide_integers(self):

       """测试整数除法"""

       result = self.calc.divide(10, 2)

       self.assertEqual(result, 5)

   

   def test_divide_floats(self):

       """测试浮点数除法"""

       result = self.calc.divide(5, 2)

       self.assertEqual(result, 2.5)

   

   def test_divide_by_zero(self):

       """测试除以零"""

       with self.assertRaises(ValueError) as context:

           self.calc.divide(5, 0)

       self.assertEqual(str(context.exception), "除数不能为零")

   

   # 测试历史记录

   def test_history(self):

       """测试历史记录功能"""

       self.calc.add(1, 2)

       self.calc.subtract(5, 3)

       self.calc.multiply(2, 3)

       

       history = self.calc.get_history()

       self.assertEqual(len(history), 3)

       self.assertIn("1 + 2 = 3", history)

       self.assertIn("5 - 3 = 2", history)

       self.assertIn("2 × 3 = 6", history)

   

   def test_clear_history(self):

       """测试清除历史记录"""

       self.calc.add(1, 2)

       self.calc.clear_history()

       self.assertEqual(len(self.calc.get_history()), 0)

   

   # 参数化测试示例(使用subTest)

   def test_add_parameterized(self):

       """参数化测试加法"""

       test_cases = [

           (1, 2, 3),

           (-1, 1, 0),

           (0, 0, 0),

           (100, 200, 300),

           (2.5, 3.5, 6.0)

       ]

       

       for a, b, expected in test_cases:

           with self.subTest(a=a, b=b, expected=expected):

               result = self.calc.add(a, b)

               self.assertEqual(result, expected)

   

   # 跳过测试

   @unittest.skip("暂时跳过这个测试")

   def test_skip_example(self):

       """跳过测试示例"""

       self.fail("这个测试应该被跳过")

   

   @unittest.skipIf(sys.version_info < (3, 7), "需要Python 3.7或更高版本")

   def test_skip_if_example(self):

       """条件跳过测试示例"""

       result = self.calc.add(3, 4)

       self.assertEqual(result, 7)


# 模拟对象测试

class TestWithMock(unittest.TestCase):

   """使用模拟对象的测试"""

   

   def test_file_operations_with_mock(self):

       """测试文件操作(使用模拟)"""

       from unittest.mock import mock_open, patch

       

       # 模拟文件内容

       mock_content = "line1\nline2\nline3"

       

       with patch('builtins.open', mock_open(read_data=mock_content)):

           with open('test.txt', 'r') as f:

               content = f.read()

           

           self.assertEqual(content, mock_content)

   

   def test_api_call_with_mock(self):

       """测试API调用(使用模拟)"""

       from unittest.mock import MagicMock, patch

       import requests

       

       # 创建模拟响应

       mock_response = MagicMock()

       mock_response.status_code = 200

       mock_response.json.return_value = {'success': True, 'data': 'test'}

       

       # 模拟requests.get

       with patch('requests.get', return_value=mock_response):

           response = requests.get('https://api.example.com/data')

           

           self.assertEqual(response.status_code, 200)

           self.assertEqual(response.json()['success'], True)


# 性能测试

class TestPerformance(unittest.TestCase):

   """性能测试"""

   

   def test_performance_of_algorithm(self):

       """算法性能测试"""

       import time

       

       # 测试不同算法的性能

       n = 10000

       

       # 测试列表生成

       start_time = time.time()

       list_comp = [i**2 for i in range(n)]

       list_comp_time = time.time() - start_time

       

       start_time = time.time()

       map_result = list(map(lambda x: x**2, range(n)))

       map_time = time.time() - start_time

       

       # 验证结果相同

       self.assertEqual(list_comp, map_result)

       

       # 性能断言(可选的)

       if list_comp_time < map_time:

           print(f"列表推导式更快: {list_comp_time:.6f} vs {map_time:.6f}")

       else:

           print(f"map函数更快: {map_time:.6f} vs {list_comp_time:.6f}")


# 集成测试

class TestIntegration(unittest.TestCase):

   """集成测试"""

   

   def test_calculator_integration(self):

       """计算器集成测试"""

       calc = Calculator()

       

       # 执行一系列操作

       result1 = calc.add(10, 5)        # 15

       result2 = calc.subtract(result1, 3)  # 12

       result3 = calc.multiply(result2, 2)  # 24

       result4 = calc.divide(result3, 4)    # 6

       

       self.assertEqual(result4, 6)

       

       # 验证历史记录

       history = calc.get_history()

       self.assertEqual(len(history), 4)

       self.assertIn("10 + 5 = 15", history)

       self.assertIn("15 - 3 = 12", history)

       self.assertIn("12 × 2 = 24", history)

       self.assertIn("24 ÷ 4 = 6", history)


# 测试套件

def create_test_suite():

   """创建测试套件"""

   suite = unittest.TestSuite()

   

   # 添加测试类

   suite.addTest(unittest.makeSuite(TestCalculator))

   suite.addTest(unittest.makeSuite(TestWithMock))

   suite.addTest(unittest.makeSuite(TestPerformance))

   suite.addTest(unittest.makeSuite(TestIntegration))

   

   return suite


# 运行测试

if __name__ == '__main__':

   # 方式1: 使用unittest.main()

   # unittest.main(verbosity=2)

   

   # 方式2: 使用测试套件

   runner = unittest.TextTestRunner(verbosity=2)

   

   # 运行所有测试

   print("=== 运行所有测试 ===")

   all_suite = unittest.defaultTestLoader.discover('.', pattern='test_*.py')

   runner.run(all_suite)

   

   # 运行特定测试类

   print("\n=== 运行Calculator测试 ===")

   calc_suite = unittest.TestLoader().loadTestsFromTestCase(TestCalculator)

   runner.run(calc_suite)

   

   # 运行单个测试方法

   print("\n=== 运行单个测试方法 ===")

   single_test = TestCalculator('test_add_integers')

   runner.run(unittest.TestSuite([single_test]))

5.2 调试技巧

python

# debugging_examples.py


import pdb

import logging

import traceback

import sys

import inspect


# 1. 使用print调试

def debug_with_print():

   """使用print语句调试"""

   print("=== 使用print调试 ===")

   

   def calculate_stats(numbers):

       # 添加print语句查看变量值

       print(f"输入: {numbers}")

       

       if not numbers:

           print("输入为空")

           return None

       

       total = sum(numbers)

       print(f"总和: {total}")

       

       average = total / len(numbers)

       print(f"平均值: {average}")

       

       return {'total': total, 'average': average}

   

   # 测试

   data = [1, 2, 3, 4, 5]

   result = calculate_stats(data)

   print(f"结果: {result}")


# 2. 使用pdb调试器

def debug_with_pdb():

   """使用pdb调试器"""

   print("\n=== 使用pdb调试 ===")

   

   def complex_calculation(a, b):

       # 设置断点

       # pdb.set_trace()  # 取消注释以使用

       

       result = 0

       for i in range(a):

           for j in range(b):

               # 这里可能会出错

               try:

                   result += i / (j + 1)

               except ZeroDivisionError:

                   print(f"零除错误: i={i}, j={j}")

                   # pdb.post_mortem()  # 进入事后调试

       

       return result

   

   # pdb常用命令:

   # h(elp) - 显示帮助

   # n(ext) - 执行下一行

   # s(tep) - 进入函数

   # c(ontinue) - 继续执行到下一个断点

   # l(ist) - 显示当前代码

   # p - 打印变量

   # q(uit) - 退出调试器

   

   print("取消注释pdb.set_trace()来使用调试器")


# 3. 使用logging调试

def debug_with_logging():

   """使用logging模块调试"""

   print("\n=== 使用logging调试 ===")

   

   # 配置logging

   logging.basicConfig(

       level=logging.DEBUG,

       format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',

       handlers=[

           logging.FileHandler('debug.log'),

           logging.StreamHandler()

       ]

   )

   

   logger = logging.getLogger(__name__)

   

   def process_data(data):

       logger.debug(f"开始处理数据: {data}")

       

       if not data:

           logger.warning("数据为空")

           return None

       

       try:

           total = sum(data)

           logger.debug(f"计算总和: {total}")

           

           average = total / len(data)

           logger.debug(f"计算平均值: {average}")

           

           return {'total': total, 'average': average}

       

       except Exception as e:

           logger.error(f"处理数据时出错: {e}", exc_info=True)

           return None

   

   # 测试

   data = [10, 20, 30, 40, 50]

   result = process_data(data)

   logger.info(f"处理结果: {result}")

   

   # 测试错误情况

   process_data([])


# 4. 异常处理与调试

def debug_with_exceptions():

   """使用异常处理调试"""

   print("\n=== 异常处理调试 ===")

   

   def divide_numbers(a, b):

       try:

           result = a / b

           return result

       except ZeroDivisionError as e:

           print(f"零除错误: {e}")

           print(f"堆栈跟踪:")

           traceback.print_exc()

           return None

       except TypeError as e:

           print(f"类型错误: {e}")

           return None

   

   # 测试

   print(f"正常情况: {divide_numbers(10, 2)}")

   print(f"零除错误: {divide_numbers(10, 0)}")

   print(f"类型错误: {divide_numbers(10, '2')}")


# 5. 使用断言调试

def debug_with_assertions():

   """使用断言调试"""

   print("\n=== 使用断言调试 ===")

   

   def calculate_discount(price, discount_percent):

       # 前置条件断言

       assert price > 0, f"价格必须大于0,实际: {price}"

       assert 0 <= discount_percent <= 100, f"折扣必须在0-100之间,实际: {discount_percent}"

       

       discount_amount = price * (discount_percent / 100)

       final_price = price - discount_amount

       

       # 后置条件断言

       assert final_price >= 0, f"最终价格不能为负,实际: {final_price}"

       assert final_price <= price, f"最终价格不能高于原价,实际: {final_price}"

       

       return final_price

   

   # 测试

   try:

       print(f"正常折扣: {calculate_discount(100, 20):.2f}")

       print(f"全价: {calculate_discount(100, 0):.2f}")

       

       # 这些会触发断言

       # calculate_discount(-100, 20)  # 价格不能为负

       # calculate_discount(100, 150)  # 折扣超出范围

   except AssertionError as e:

       print(f"断言错误: {e}")


# 6. 代码性能调试

def debug_performance():

   """性能调试"""

   print("\n=== 性能调试 ===")

   

   import time

   import cProfile

   import pstats

   import io

   

   def slow_function(n):

       """性能较差的函数"""

       result = []

       for i in range(n):

           for j in range(n):

               result.append((i, j, i * j))

       return result

   

   def fast_function(n):

       """性能较好的函数"""

       result = [(i, j, i * j) for i in range(n) for j in range(n)]

       return result

   

   # 使用time测量

   print("使用time测量:")

   

   n = 100

   

   start = time.time()

   slow_result = slow_function(n)

   slow_time = time.time() - start

   print(f"慢函数: {slow_time:.4f}秒,结果数: {len(slow_result)}")

   

   start = time.time()

   fast_result = fast_function(n)

   fast_time = time.time() - start

   print(f"快函数: {fast_time:.4f}秒,结果数: {len(fast_result)}")

   

   # 使用cProfile分析

   print("\n使用cProfile分析慢函数:")

   

   profiler = cProfile.Profile()

   profiler.enable()

   slow_function(50)  # 使用较小的n避免运行太久

   profiler.disable()

   

   # 输出分析结果

   stats_stream = io.StringIO()

   stats = pstats.Stats(profiler, stream=stats_stream).sort_stats('cumulative')

   stats.print_stats(10)  # 显示前10个

   print(stats_stream.getvalue())


# 7. 内存使用调试

def debug_memory():

   """内存使用调试"""

   print("\n=== 内存使用调试 ===")

   

   import sys

   import gc

   

   class MemoryIntensive:

       def __init__(self, size):

           self.data = [i for i in range(size)]

       

       def __repr__(self):

           return f"MemoryIntensive(size={len(self.data)})"

   

   def check_memory():

       """检查内存使用"""

       print("创建对象前内存使用:")

       for obj in gc.get_objects():

           if isinstance(obj, MemoryIntensive):

               print(f"  找到: {obj}, 大小: {sys.getsizeof(obj)}字节")

       

       # 创建大对象

       print("\n创建大对象...")

       big_obj = MemoryIntensive(1000000)

       

       print(f"对象大小: {sys.getsizeof(big_obj)}字节")

       print(f"对象数据大小: {sys.getsizeof(big_obj.data)}字节")

       

       # 删除对象

       print("\n删除对象...")

       del big_obj

       

       # 强制垃圾回收

       gc.collect()

       

       print("垃圾回收后,再次检查...")

       found = False

       for obj in gc.get_objects():

           if isinstance(obj, MemoryIntensive):

               print(f"  仍存在: {obj}")

               found = True

       

       if not found:

           print("  对象已正确释放")

   

   check_memory()


# 8. 交互式调试工具

def interactive_debugging():

   """交互式调试"""

   print("\n=== 交互式调试工具 ===")

   

   # 使用inspect模块

   def example_function(a, b, c=10, *args, **kwargs):

       """示例函数"""

       result = a + b + c + sum(args)

       return result

   

   print("使用inspect模块:")

   

   # 获取函数签名

   sig = inspect.signature(example_function)

   print(f"函数签名: {sig}")

   

   # 获取参数信息

   print("参数信息:")

   for name, param in sig.parameters.items():

       print(f"  {name}: {param}")

   

   # 获取源代码

   print("\n函数源代码:")

   print(inspect.getsource(example_function))

   

   # 获取调用栈

   print("\n当前调用栈:")

   for frame_info in inspect.stack()[:3]:  # 只显示前3层

       print(f"  文件: {frame_info.filename}, 行: {frame_info.lineno}, 函数: {frame_info.function}")


# 9. 自定义调试装饰器

def create_debug_decorator():

   """创建调试装饰器"""

   print("\n=== 自定义调试装饰器 ===")

   

   import functools

   import time

   

   def debug_decorator(func):

       """调试装饰器"""

       @functools.wraps(func)

       def wrapper(*args, **kwargs):

           print(f"\n调用函数: {func.__name__}")

           print(f"参数: args={args}, kwargs={kwargs}")

           

           start_time = time.time()

           

           try:

               result = func(*args, **kwargs)

               end_time = time.time()

               

               print(f"返回值: {result}")

               print(f"执行时间: {end_time - start_time:.6f}秒")

               

               return result

           

           except Exception as e:

               end_time = time.time()

               print(f"异常: {e}")

               print(f"执行时间: {end_time - start_time:.6f}秒")

               raise

       

       return wrapper

   

   # 使用装饰器

   @debug_decorator

   def example_debug_function(x, y, operation='add'):

       """示例函数"""

       if operation == 'add':

           return x + y

       elif operation == 'multiply':

           return x * y

       else:

           raise ValueError(f"未知操作: {operation}")

   

   # 测试

   print("正常调用:")

   result1 = example_debug_function(10, 20, operation='add')

   print(f"结果: {result1}")

   

   print("\n乘法调用:")

   result2 = example_debug_function(10, 20, operation='multiply')

   print(f"结果: {result2}")

   

   print("\n错误调用:")

   try:

       example_debug_function(10, 20, operation='divide')

   except ValueError as e:

       print(f"捕获异常: {e}")


# 主程序

if __name__ == "__main__":

   print("=== Python调试技巧示例 ===")

   

   # 运行各个示例

   debug_with_print()

   debug_with_pdb()

   debug_with_logging()

   debug_with_exceptions()

   debug_with_assertions()

   debug_performance()

   debug_memory()

   interactive_debugging()

   create_debug_decorator()

   

   print("\n=== 调试技巧总结 ===")

   print("1. print: 最简单的调试方法,适合快速查看变量值")

   print("2. pdb: 强大的交互式调试器,适合复杂调试")

   print("3. logging: 适合生产环境的调试,可以记录到文件")

   print("4. 异常处理: 捕获和处理异常,获取详细错误信息")

   print("5. 断言: 验证代码假设,及早发现错误")

   print("6. 性能分析: 使用time和cProfile分析性能瓶颈")

   print("7. 内存调试: 跟踪内存使用,发现内存泄漏")

   print("8. 交互式工具: 使用inspect等工具动态检查代码")

   print("9. 自定义装饰器: 创建可重用的调试工具")

第六部分:项目结构与代码组织


明扬工控商城

推荐阅读:

Python中级教程 第六课:高级特性与性能优化 2/2

Python中级教程 第六课:高级特性与性能优化 1/2

Python中级教程 第五课:异步编程与并发 2/2

Python中级教程 第五课:异步编程与并发 1/2

Python中级教程 第四课:上下文管理器、生成器与迭代器

Python中级教程 第三课:面向对象编程深入与装饰器

热门标签:
Python中级教程 第七课:网络编程、并发与项目实践 5/7.docx

将本文的Word文档下载到电脑

推荐度:

下载

全部评论

请登录
产业新闻-明扬资讯网
科技资讯-明扬资讯网