飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 认识Python

    • Python 历史与特点
    • Python 2 与 Python 3 的核心差异
    • 安装与运行 Python 2.7.18
    • 编码规范 PEP 8
  • 第2章 基础语法

    • 变量与对象
    • 数字类型
    • 字符串 str
    • Unicode 字符串
    • 运算符
    • 空值 None
  • 第3章 流程控制

    • if 条件语句
    • if-else 条件语句
    • if-elif-else 多分支
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 与 xrange
    • 循环控制:break、continue、pass
    • 循环 else 子句
  • 第4章 数据结构

    • 列表基础
    • 列表方法
    • 列表推导式
    • 元组
    • 字典基础
    • 字典方法
    • 字典循环技巧
    • 集合
    • 序列解包
    • 序列比较
  • 第5章 函数

    • 定义函数
    • 参数传递机制
    • 默认参数
    • 关键字参数
    • 可变参数
    • Lambda 表达式
    • 文档字符串
    • 函数对象
  • 第6章 模块与包

    • import 导入
    • 模块搜索路径
    • name 与主程序
    • 编译文件 .pyc 与 .pyo
    • 包结构
    • dir() 函数
  • 第7章 文件与IO

    • 打开与关闭文件
    • 文件读写方法
    • with 上下文管理器
    • 格式化输出:% 操作符
    • 格式化输出:str.format()
    • JSON 序列化
  • 第8章 面向对象

    • 类定义与实例化
    • init 构造方法
    • 类变量与实例变量
    • 方法调用与 self
    • 继承基础
    • 多重继承
    • 新式类与旧式类
    • 私有变量与名称改写
    • 属性装饰器 property
    • 类方法与静态方法
    • 魔术方法
    • 空类与数据记录
  • 第9章 异常处理

    • 异常类型
    • try-except
    • try-except-else-finally
    • 抛出异常 raise
    • 自定义异常
    • with 语句与上下文管理器
  • 第10章 迭代器与生成器

    • 迭代器协议
    • 生成器函数
    • 生成器表达式
    • itertools模块
  • 第11章 标准库精要

    • os模块
    • sys模块
    • datetime模块
    • re模块
    • json模块
    • collections模块
    • math与random模块
    • urllib2与网络请求
    • subprocess与命令执行
    • threading与并发
    • unittest与测试
    • 虚拟环境与包管理
  • 第12章 工程实践

    • 调试技巧
    • 性能分析
    • 文档与注释
    • 下一步学习

unittest与测试

unittest 是 Python 标准库中的单元测试框架,灵感来自 Java 的 JUnit。它支持测试用例组织、断言、测试夹具(fixture)、测试套件等功能,是 Python 2.7 中编写自动化测试的标准工具。

基本测试用例

import unittest

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

class TestMathOperations(unittest.TestCase):
    def test_add_positive(self):
        self.assertEqual(add(1, 2), 3)
    
    def test_add_negative(self):
        self.assertEqual(add(-1, -2), -3)
    
    def test_add_zero(self):
        self.assertEqual(add(0, 5), 5)

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

运行测试:

python test_math.py
python test_math.py -v       # 详细输出

常用断言

import unittest

class TestAssertions(unittest.TestCase):
    def test_equal(self):
        self.assertEqual(1 + 1, 2)
        self.assertNotEqual(1 + 1, 3)
    
    def test_true_false(self):
        self.assertTrue(1 == 1)
        self.assertFalse(1 == 2)
    
    def test_none(self):
        self.assertIsNone(None)
        self.assertIsNotNone("hello")
    
    def test_in(self):
        self.assertIn(2, [1, 2, 3])
        self.assertNotIn(4, [1, 2, 3])
    
    def test_raises(self):
        with self.assertRaises(ZeroDivisionError):
            1 / 0
    
    def test_almost_equal(self):
        self.assertAlmostEqual(0.1 + 0.2, 0.3, places=7)
    
    def test_greater(self):
        self.assertGreater(5, 3)
        self.assertLess(3, 5)

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

测试夹具(Fixture)

setUp 和 tearDown 在每个测试方法前后执行:

import unittest
import tempfile
import os

class TestFileOperations(unittest.TestCase):
    def setUp(self):
        """每个测试前创建临时文件。"""
        self.temp_file = tempfile.mkstemp()[1]
    
    def tearDown(self):
        """每个测试后清理。"""
        if os.path.exists(self.temp_file):
            os.remove(self.temp_file)
    
    def test_write_read(self):
        with open(self.temp_file, "w") as f:
            f.write("hello")
        
        with open(self.temp_file, "r") as f:
            content = f.read()
        
        self.assertEqual(content, "hello")

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

setUpClass 和 tearDownClass(类方法,Python 2.7+)在所有测试前后执行一次:

import unittest

class TestDatabase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.connection = create_test_database()
    
    @classmethod
    def tearDownClass(cls):
        cls.connection.close()
    
    def test_query(self):
        result = self.connection.query("SELECT 1")
        self.assertEqual(result, [(1,)])

跳过测试

import unittest
import sys

class TestPlatformSpecific(unittest.TestCase):
    @unittest.skip("Not implemented yet")
    def test_future_feature(self):
        pass
    
    @unittest.skipIf(sys.platform == "win32", "Not supported on Windows")
    def test_unix_feature(self):
        import pwd
        pwd.getpwuid(0)
    
    @unittest.skipUnless(sys.version_info >= (2, 7), "Requires Python 2.7+")
    def test_new_feature(self):
        pass

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

测试组织

测试发现:

# 自动发现当前目录下的测试
python -m unittest discover

# 指定目录和模式
python -m unittest discover -s tests -p "test_*.py" -v

测试套件:

import unittest
from test_math import TestMathOperations
from test_string import TestStringOperations

suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestMathOperations))
suite.addTest(unittest.makeSuite(TestStringOperations))

runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)

Mock(模拟对象)

Python 2.7 标准库没有 unittest.mock(Python 3.3+),需要安装 mock 包:

pip install mock
import unittest
try:
    from unittest.mock import Mock, patch
except ImportError:
    from mock import Mock, patch

class TestAPIClient(unittest.TestCase):
    @patch("urllib2.urlopen")
    def test_fetch_data(self, mock_urlopen):
        # 模拟响应
        mock_response = Mock()
        mock_response.read.return_value = '{"status": "ok"}'
        mock_urlopen.return_value = mock_response
        
        result = fetch_data("http://api.example.com")
        self.assertEqual(result["status"], "ok")
        mock_urlopen.assert_called_once()

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

实际应用

测试驱动开发示例:

import unittest

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

class TestPrime(unittest.TestCase):
    def test_small_primes(self):
        self.assertTrue(is_prime(2))
        self.assertTrue(is_prime(3))
        self.assertTrue(is_prime(5))
        self.assertTrue(is_prime(7))
    
    def test_non_primes(self):
        self.assertFalse(is_prime(1))
        self.assertFalse(is_prime(4))
        self.assertFalse(is_prime(9))
    
    def test_edge_cases(self):
        self.assertFalse(is_prime(0))
        self.assertFalse(is_prime(-1))

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

测试 Web 应用:

import unittest
from myapp import create_app

class TestWebApp(unittest.TestCase):
    def setUp(self):
        self.app = create_app(testing=True)
        self.client = self.app.test_client()
    
    def test_homepage(self):
        response = self.client.get("/")
        self.assertEqual(response.status_code, 200)
        self.assertIn("Welcome", response.data)
    
    def test_404(self):
        response = self.client.get("/nonexistent")
        self.assertEqual(response.status_code, 404)

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

测试最佳实践

  • 测试方法名以 test_ 开头
  • 每个测试只验证一个概念
  • 使用 setUp 创建共享的测试数据
  • 测试应该是独立的,不依赖执行顺序
  • 运行要快,便于频繁执行
  • 覆盖正常路径和边界情况

与 pytest 的选择

特性unittestpytest
标准库是否(需安装)
断言风格方法调用原生 assert
插件生态少丰富
参数化测试较繁琐@pytest.mark.parametrize
兼容性Python 2/3Python 2.7+(需旧版本)

Python 2.7 项目中,unittest 无需安装,是稳妥的选择。如果允许安装第三方库,pytest 的语法更简洁。

上一页
threading与并发
下一页
虚拟环境与包管理