Python 日志、调试与单元测试
使用 logging
模块打印日志
import logging
logging.basicConfig(level=logging.INFO)
"""
logging 共有四个级别:
logging.INFO
logging.WARNING
logging.DEBUG
logging.ERROR
"""
n = 2
m = 0
logging.info(f"n:{n}, m:{m}")
"""
INFO:root:n:2, m:0
"""
用 pdb
调试代码
单步调试
# pdb_demo.py
a = 2
b = 3
c = a + b
python -m pdb pdb_demo.py
> /root/note/pdb_demo.py(1)<module>()
-> a = 2
(Pdb) l # 查看代码
1 -> a = 2
2 b = 3
3 c = a + b
[EOF]
(Pdb) n # 单步运行
> /root/note/pdb_demo.py(2)<module>()
-> b = 3
(Pdb) p a # 查看变量 a
2
(Pdb) p b # 查看变量 b,此时还未执行到变量b
*** NameError: name 'b' is not defined
(Pdb) n # 单步执行
> /root/note/pdb_demo.py(3)<module>()
-> c = a + b
(Pdb) p b
3
(Pdb) n
--Return--
> /root/note/pdb_demo.py(3)<module>()->None
-> c = a + b
(Pdb) p c
5
(Pdb) n
--Return--
> <string>(1)<module>()->None
(Pdb) q # 退出
打断点调试
# pdb_demo.py
import pdb
a = 2
b = 3
pdb.set_trace()
c = a + b
python pdb_demo.py
> /root/note/pdb_demo.py(5)<module>()
-> c = a + b
(Pdb) p a
2
(Pdb) p b
3
(Pdb) p c
*** NameError: name 'c' is not defined
(Pdb) n
--Return--
> /root/note/pdb_demo.py(5)<module>()->None
-> c = a + b
(Pdb) p c
5
(Pdb) n
单元测试
我们开发的项目会不断迭代,每次迭代既要保证新的功能可以用,还要保证原有功能依然没问题。
通常我们会编写相应的单元测试,这样在每次迭代后,如果新的代码可以通过所有测试用例,那么新的代码就没问题。
假如编写了一个继承自 dict
,但可以用 .
读取属性,可以用来存储数据的类 Storage
:
# demo_storage.py
class Storage(dict):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(f"key:{key} not exists")
def __setattr__(self, key, value):
self[key] = value
单元测试可以这样写:
# test_storage.py
import unittest
from demo_storage import Storage
class TestStorage(unittest.TestCase):
def test_init(self):
"""
测试初始化函数
"""
st = Storage(
a = 1,
b = "test"
)
self.assertEqual(st.a, 1) # 断言值相等
self.assertEqual(st.b, "test")
self.assertTrue(
isinstance(st, dict)
)
def test_key(self):
"""
测试字典方式取值
"""
st = Storage()
st["key"] = "value"
self.assertEqual(st.key, "value")
def test_attr(self):
"""
测试点方式取值
"""
st = Storage()
st.key = "value"
self.assertTrue("key" in st)
self.assertEqual(st["key"], "value")
def test_keyerror(self):
st = Storage()
with self.assertRaises(KeyError): # 断言抛出指定异常
value = st["empty"]
def test_attrerror(self):
st = Storage()
with self.assertRaises(AttributeError):
value = st.empty
if __name__ == "__main__":
unittest.main()
运行单元测试:
$ python test_storage.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
$ python -m unittest test_storage.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
有些业务需要连接数据库,这种情况可以编写 setUp()
和 tearDown()
函数,分别表示测试开始和测试结束要运行的逻辑。
...
def setUp(self):
print("set_up")
def tearDown(self):
print("tear_down")
...
运行测试效果:
$ python -m unittest test_storage.py
set_up
tear_down
.set_up
tear_down
.set_up
tear_down
.set_up
tear_down
.set_up
tear_down
.
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
(本文完)