Skip to main content

Python Unit Test

介绍

Python unittest 是 Python 自带的单元测试框架,麻雀虽小五脏俱全,能简单的上手使用,功能也逐渐完整。本篇就简单介绍一下

单元测试

类与函数

unittest 提供了 unittest.TestCase 类,可以通过继承这个类来写测试用例。下面是一个最简单的样例,一个类在继承 unittest.TestCase 之后,所有以 test 开头的方法都会被当作一个测试用例,在主函数中执行 unittest.main() 就会运行当前文件中所有的测试用例

test.py
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 判断一个值是否为真,其他用于判断的方法,笔者会在断言方法In-Page Anchor一节中介绍

当然,这只是使用 unittest 最简单的一种方法,后面笔者会介绍其他用法

脚手架

脚手架,或者有翻译韦夹具,即 fixture ,是在 unittest.TestCase 中用于设置上下文的一系列方法,设想下列场景:

  • 临时文件/目录
    • 在测试开始前创建一个临时文件,在测试结束后删除它
    • 创建测试目录树、挂载虚拟文件系统、修改文件权限、创建符号链接、模拟磁盘配额或只读卷
  • 数据库相关
    • 建立连接、开始事务并回滚(或使用测试专用 DB)、在测试前后做 schema migration 与种子数据(fixtures)加载/清理
  • 网络/HTTP 服务
    • 启动一个本地模拟 HTTP 服务(例如 http.server 或 Flask 测试服务器)、准备请求/响应模板、停止服务与端口释放
  • 环境变量设置
  • 认证/会话/凭据
    • 生成临时 API token、模拟登录 session、创建测试用户并在测试后删除
  • 其他外部上下文
    • 启动/停止本地依赖进程(消息队列 RabbitMQ、数据库、Selenium server、Docker 容器),确保进程退出与端口回收

基本也就是我们会使用到 with 的场景

脚手架这里介绍 setUptearDown,类比 __enter____exit__。下面就是一个数据库连接的样例

fixture
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()
note

在运行每个测试时,setUp()tearDown()__init__() 会被调用一次

断言方法

这部分更像一个 CheatSheet,随用随找即可。本来想放到最后,但是好像也属于 类与函数 的范畴就放这里了。可以直接跳转到用法In-Page Anchor继续阅读

用法

命令行

是的,可以用命令行直接测试某个具体的用例,参考下面这个类。包括两个测试用例:

  • 判断是否有 user 表
  • 判断 user 表中是否有 id 列
test_db.py
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 表,即单独执行某个测试用例,可以用下面的命令来直接运行

可以注意到,即使是单独执行某个测试用例,setUptearDown 依然会被调用,不会出现配置方面的错误

tip

例如,若某测试用例排在后面且前序耗时较长,单独运行该用例可更快定位问题

命令行接口有部分参数,可以使用 python -m unittest -h 查看

Test Suite

案例

History

Buy me a coffee ☕:
This article is licensed under CC 4.0 BY-SAExternal Link