0%

Python单元测试

unittest简介

unittest是一个Python单元测试框架。它受到 JUnit 的启发,与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化,配置共享和关机代码测试。支持将测试样例聚合到测试集中,并将测试与报告框架独立。

主要参考 unittest - 单元测试框架

脚本编写

新建一个 tests/test_demo.py 脚本,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import unittest

class TestStringMethods(unittest.TestCase):

def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')

def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())

def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)

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

继承 unittest.TestCase 就创建了一个测试样例。上述三个独立的测试是三个类的方法,这些方法的命名都以 test 开头。 这个命名约定告诉测试运行者类的哪些方法表示测试。

每个测试的关键是:调用 assertEqual() 来检查预期的输出; 调用 assertTrue() 或 assertFalse() 来验证一个条件;调用 assertRaises() 来验证抛出了一个特定的异常。

通过 setUp() 和 tearDown() 方法,可以设置测试开始前与完成后需要执行的指令。

最后的代码块中,演示了运行测试的一个简单的方法。 unittest.main() 提供了一个测试脚本的命令行接口。

执行测试

方法一:脚本级别调用(直接运行脚本)

1
2
3
python tests/test_demo.py
python tests/test_demo.py -v
python -m unittest tests/test_demo.py

方法二:包、模块、类和方法级别调用

1
2
3
4
5
6
7
8
# 包级别调用(进入tests目录查找所有test_*.py文件并运行)
python -m unittest discover -s tests -p "test_*.py"
# 模块级别调用
python -m unittest tests.test_demo
# 类级别调用
python -m unittest tests.test_demo.TestStringMethods
# 方法级别调用
python -m unittest tests.test_demo.TestStringMethods.test_upper

单元测试之mock

单元测试时,如果涉及网络请求,建议使用mock模块来模拟。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import json
import unittest
from unittest import mock
import requests

def request_system(method: str, url: str, data={}):
headers = {
'Content-Type': 'application/json'
}
response = requests.request(method=method, url=url, headers=headers, data=json.dumps(data))
return response

def login(username: str, password: str):
# 以下接口暂时访问不通
LOGIN_URL='http://127.0.0.1/login'
data = {
'username': username,
'password': password
}
response = request_system('POST', LOGIN_URL)
if response.status == 200:
return json.loads(response.text)
else:
return None

class TestMockMethods(unittest.TestCase):
def test_request_system(self):
mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.text = json.dumps({"code": 0, "msg":"success"})
request_system = mock.Mock(return_value=mock_response)

response = request_system(method='POST',url='http://127.0.0.1/')
print(response.status_code)
print(response.text)
self.assertEqual(200, response.status_code)

def test_login(self):
login = mock.Mock(return_value={"code": 0, "msg":"success"})
data = login(username='voidking',password='voidking')
print(data)
self.assertEqual(0, data.get('code'))

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

这种模拟测试方式很巧妙,适合测试访问第三方接口,但是并不会真正发出请求。
另一个问题来了,Python怎么测试自己的接口?不知道。
想到以前使用Beego框架进行开发,它的单元测试就很巧妙,先在测试数据库插入数据,然后通过beego.BeeApp.Handlers.ServeHTTP(w, r)把自己临时启动起来,最后自己的单元测试调用自己的接口。
其中的关键在于把自己启动起来,理论上Python也能做到。

  • 本文作者: 好好学习的郝
  • 本文链接: https://www.voidking.com/dev-python-unittest/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!源站会及时更新知识点及修正错误,阅读体验也更好。欢迎分享,欢迎收藏~