单元测试
单元测试的目的并不是查找bug,而是帮助我们更好的设计我们的代码,如何合理的来拆分我们的代码。
单元测试vs集成测试
集成测试检查各个组件间协作运行是否正常,单元测试检查应用程序中的一个某一个小的功能模块。
相关工具
以python为例
unittestnoseornose2pytest
其中,nose和nose2基于unittest, 如果使用python2可以使用前两中,pytest要求python3.7+。pytest有较多插件, 显示内容更为丰富一些。
用官方例子比较一下:
# content of test_sample.py
def inc(x):
return x + 1
def test_answer():
assert inc(3) == 5
直接运行pytest
[garlic@centos8 pytest]$ pytest
========================================= test session starts =========================================
platform linux -- Python 3.6.8, pytest-7.0.1, pluggy-1.0.0
rootdir: /home/garlic/pytest/pytest
collected 1 item
test_sample.py F [100%]
============================================== FAILURES ===============================================
_____________________________________________ test_answer _____________________________________________
def test_answer():
> assert inc(3) == 5
E assert 4 == 5
E + where 4 = inc(3)
test_sample.py:7: AssertionError
======================================= short test summary info =======================================
FAILED test_sample.py::test_answer - assert 4 == 5
========================================== 1 failed in 0.03s ==========================================
[garlic@centos8 pytest]$ cat test_sample.py
# content of test_sample.py
def inc(x):
return x + 1
def test_answer():
assert inc(3) == 5
如果用unittest要写成下面的样子:
# content of test_sample.py
import unittest
def inc(x):
return x + 1
class TestInc(unittest.TestCase):
def test_answer(self):
assert inc(3) == 5
if __name__ == '__main__':
unittest.main()
输出结果如下:
[garlic@centos8 pytest]$ python3 test_sample.py
F
======================================================================
FAIL: test_answer (__main__.TestInc)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_sample.py", line 10, in test_answer
assert inc(3) == 5
AssertionError
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
比较而言, pytest编写更加简单,显示结果更加丰富, 更加详细的说明可以参考
- https://realpython.com/pytest-python-testing/#how-to-install-pytest
- https://docs.pytest.org/en/latest/
一些规则
根据实际情况选择好了工具, 其实重要的是编写测试用例,下面是一些可以参考的单元测试最佳实践:
- Test only one code unit at a time
- Don’t make unnecessary assertions
- Make each test independent to all the others
- Mock out all external services and state
- Don’t unit-test configuration setting
- Use the most appropriate assertion methods
- Do not print anything out in unit tests
相关更详细的可以参考:
- http://www.kyleblaney.com/junit-best-practices/
- https://stackify.com/unit-testing-basics-best-practices/
- https://dzone.com/articles/unit-testing-best-practices
目录布局
项目代码ke’y
project/
│
├── my_app/
│ └── __init__.py
│
└── tests/
|
└── unit/
| ├── __init__.py
| └── test_sum.py
|
└── integration/
|
├── fixtures/
| ├── test_basic.json
| └── test_complex.json
|
├── __init__.py
└── test_integration.py
分为源码,单元测试unit,集成测试integration,外部依赖数据可以放到fixtures中。如果测试前有相关依赖可以正再setUp方法中, 例子中依赖json文件。
import unittest
class TestBasic(unittest.TestCase):
def setUp(self):
# Load test data
self.app = App(database='fixtures/test_basic.json')
def test_customer_count(self):
self.assertEqual(len(self.app.customers), 100)
def test_existence_of_customer(self):
customer = self.app.get_customer(id=10)
self.assertEqual(customer.name, "Org XYZ")
self.assertEqual(customer.address, "10 Red Road, Reading")
class TestComplexData(unittest.TestCase):
def setUp(self):
# load test data
self.app = App(database='fixtures/test_complex.json')
def test_customer_count(self):
self.assertEqual(len(self.app.customers), 10000)
def test_existence_of_customer(self):
customer = self.app.get_customer(id=9999)
self.assertEqual(customer.name, u"バナナ")
self.assertEqual(customer.address, "10 Red Road, Akihabara, Tokyo")
if __name__ == '__main__':
unittest.main()
自动测试
可以通过一些工具Travis CI 完成自动化测试
language: python python: - "2.7" - "3.7" install: - pip install -r requirements.txt script: - python -m unittest discover
如果依赖一些其他package可以在requirements.txt中设置。
其他
单元测试有助于针对关键代码块逻辑的验证,可以抛开外部依赖,进行多种异常、正常的输入规则逻辑处理。使得代码更健壮。
本次任务是对之前的系统不是很熟悉, 要在原来的代码中嵌入一些逻辑判断,尝试着做了一下单元测试。使用过程中通过单元测试验证自己代码设计是否符合要求,由于对业务不是很熟悉,在同事测试后还是发现了问题, 使用单元测试对设计问题进行了快速验证,并很方便的执行之前所有单元测试用例,防止影响程序其他处理逻辑。
相比较之前做的中间业务系统,业务比较熟悉,但是系统接口调试居多,涉及到测试代码,会写一些挡板(mock), 开发完成后做下本地组装测试,可以防止集成测试,功能测试出现较大问题。
参考及引用
https://realpython.com/python-testing/#automated-vs-manual-testing
图片from陳禮樂


Comments are closed.