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

(本文完)

上一篇:ATECC508A芯片开发笔记(九):加密读写数据的流程及相应设置


下一篇:Python调试