单元测试
单元测试的目的并不是查找bug,而是帮助我们更好的设计我们的代码,如何合理的来拆分我们的代码。
单元测试vs集成测试
集成测试检查各个组件间协作运行是否正常,单元测试检查应用程序中的一个某一个小的功能模块。
相关工具
以python为例
unittest
nose
ornose2
pytest
其中,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.