Python Unit Test
介绍
Python unittest 是 Python 自带的单元测试框架,麻雀虽小五脏俱全,能简单的上手使用,功能也逐渐完整。本篇就简单介绍一下
单元测试
类与函数
unittest
提供了 unittest.TestCase
类,可以通过继承这个类来写测试用例。下面是一个最简单的样例,一个类在继承 unittest.TestCase
之后,所有以 test
开头的方法都会被当作一个测试用例,在主函数中执行 unittest.main()
就会运行当前文件中所有的测试用例
import unittest
class MyTest(unittest.TestCase):
def test_function(self):
self.assertEqual(1 + 1, 2)
def test_another_function(self):
self.assertEqual(1 + 1, 3)
class AnotherTest(unittest.TestCase):
def test_another_function(self):
self.assertTrue(False)
if __name__ == '__main__':
unittest.main()
运行上面样例的结果如下,顺序似乎是栈,没有细究。其中 assertEqual
用于判断两个值是否相等,assertTrue
判断一个值是否为真,其他用于判断的方法,笔者会在断言方法页面内跳转一节中介绍
当然,这只是使用 unittest
最简单的一种方法,后面笔者会介绍其他用法
脚手架
脚手架,或者有翻译韦夹具,即 fixture
,是在 unittest.TestCase
中用于设置上下文的一系列方法,设想下列场景:
- 临时文件/目录
- 在测试开始前创建一个临时文件,在测试结束后删除它
- 创建测试目录树、挂载虚拟文件系统、修改文件权限、创建符号链接、模拟磁盘配额或只读卷
- 数据库相关
- 建立连接、开始事务并回滚(或使用测试专用 DB)、在测试前后做 schema migration 与种子数据(fixtures)加载/清理
- 网络/HTTP 服务
- 启动一个本地模拟 HTTP 服务(例如 http.server 或 Flask 测试服务器)、准备请求/响应模板、停止服务与端口释放
- 环境变量设置
- 认证/会话/凭据
- 生成临时 API token、模拟登录 session、创建测试用户并在测试后删除
- 其他外部上下文
- 启动/停止本地依赖进程(消息队列 RabbitMQ、数据库、Selenium server、Docker 容器),确保进程退出与端口回收
基本也就是我们会使用到 with
的场景
脚手架这里介绍 setUp
和 tearDown
,类比 __enter__
和 __exit__
。下面就是一个数据库连接的样例
import unittest
import pymysql
class MyTest(unittest.TestCase):
def setUp(self):
self.connection = pymysql.connect(host, user, password, db)
def test_function(self):
with self.connection.cursor() as cursor:
cursor.execute("show tables")
result = cursor.fetchall()
self.assertIn(('user',), result)
def tearDown(self):
self.connection.close()
if __name__ == '__main__':
unittest.main()
在运行每个测试时,
setUp()
、tearDown()
和__init__()
会被调用一次
断言方法
这部分更像一个 CheatSheet
,随用随找即可。本来想放到最后,但是好像也属于 类与函数
的范畴就放这里了。可以直接跳转到用法页面内跳转继续阅读
用法
命令行
是的,可以用命令行直接测试某个具体的用例,参考下面这个类。包括两个测试用例:
- 判断是否有 user 表
- 判断 user 表中是否有 id 列
import unittest
import pymysql
class MyTest(unittest.TestCase):
def setUp(self):
self.connection = pymysql.connect(host, user, password, db)
def test_function(self):
with self.connection.cursor() as cursor:
cursor.execute("show tables")
result = cursor.fetchall()
print(f"{len(result)} tables")
self.assertIn(('user',), result)
def test_another_function(self):
with self.connection.cursor() as cursor:
cursor.execute("select id from user")
result = cursor.fetchall()
self.assertIn(('id',), result)
def tearDown(self):
self.connection.close()
if __name__ == '__main__':
unittest.main()
假设我们想单独看一下是否有这个 user 表,即单独执行某个测试用例,可以用下面的命令来直接运行
可以注意到,即使是单独执行某个测试用例,setUp
和 tearDown
依然会被调用,不会出现配置方面的错误
例如,若某测试用例排在后面且前序耗时较长,单独运行该用例可更快定位问题
命令行接口有部分参数,可以使用 python -m unittest -h
查看