一个计算机技术爱好者与学习者

0%

好好学Python:Python单元测试

1. unittest简介

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

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

2. 脚本编写

新建一个 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() 提供了一个测试脚本的命令行接口。

3. 执行测试

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

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

4. 单元测试之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也能做到。

5. 后记

python语言中的单元测试,除了使用unittest,还可以使用pytest。

FROM ChatGPT:
unittest和pytest是Python中两种流行的单元测试框架,它们有以下主要区别:

  • 语法:unittest是Python标准库中的测试框架,而pytest是一个第三方测试框架。因此,unittest的语法更加正式和严格,而pytest的语法更加简洁和灵活。
  • 自动化:pytest比unittest更加自动化和智能化。pytest可以自动发现和运行测试用例,而unittest需要手动编写测试用例的套件和运行代码。
  • 插件和扩展性:pytest提供了大量的插件和扩展功能,例如测试报告、代码覆盖率、测试并行化、测试重试等。相比之下,unittest的扩展性较弱,需要编写更多的自定义代码才能实现类似的功能。
  • 断言:unittest和pytest都提供了丰富的断言方法,用于检查测试结果是否符合预期。但是,pytest的断言方法更加灵活和易于使用,例如使用assert语句进行断言。
  • 用例参数化:pytest支持用例参数化,可以通过数据驱动的方式多次运行同一个测试用例,以检查不同的输入和输出。相比之下,unittest需要手动编写多个测试用例,以实现类似的功能。
  • 本文作者: 好好学习的郝
  • 原文链接: https://www.voidking.com/dev-python-unittest/
  • 版权声明: 本文采用 BY-NC-SA 许可协议,转载请注明出处!源站会即时更新知识点并修正错误,欢迎访问~
  • 微信公众号同步更新,欢迎关注~