Test your software, or your users will. "Test ruthlessly. Don't make your users find bugs for you."

最近看了Axb的自我修养写的关于好代码,烂代码和单元测试的一些文章,挺受启发的,结合python讲一下自己对单元测试的理解和操作。

单元测试是什么

单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

为什么要进行单元测试? 测试除了保证程序的健壮性外,是可以让你重新思考代码的设计的。引用Axb博客的话:编写单元测试的难易程度能够直接反应出代码的设计水平,能写出单元测试和写不出单元测试之间体现了编程能力上的巨大的鸿沟。无论是什么样的程序员,坚持编写一段时间的单元测试之后,都会明显感受到代码设计能力的巨大提升。

如果发现代码难以构造测试,很有可能就是接口设计不够优雅,或者耦合严重,尝试从测试的角度思考能够让我们更好地设计。单元测试同时也为重构提供了保证,比如我们想优化一个函数内部实现,更换更优的数据结构和算法,只需要重新跑一下测试就可以验证新的实现是否引入了错误或bug。

总的来说,单元测试有以下好处:确保代码质量

改善代码设计,难以测试的代码一般是设计不够简洁的代码。

保证重构不会引入新问题,以函数为单位进行重构的时候,只需要重新跑测试就基本可以保证重构没引入新问题。

测试如何影响代码设计

以上来自《编写可读代码的艺术》,需要自己实践才有体会。

python测试相关库unittest,内置库,模仿PyUnit写的,简洁易用,缺点是比较繁琐。

nose,测试发现,发现并运行测试。

pytest,笔者目前喜欢用这个,写起来很方便,并且很多知名开源项目在用,推荐。

mock, 替换掉网络调用或者 rpc 请求等

使用pytest进行python进行单元测试

python内置了一个unittest,但是写起来稍微繁琐,比如都要写一个TestCase类,还得用 assertEqual, assertNotEqual等断言方法。 而使用pytest运行测试统一用assert语句就行,兼容unittest,目前很多知名开源项目如PyPy,Sentry也都在用。关于pytest的使用可以参考其官方文档,虽然有很多高级特性,但是掌握其中一小部分基本就够用了。

下面是py.test的基本用法,以常见的两种测试类型(验证返回值和抛出异常)为例:

def add(a, b):

"""return a + b

Args:

a (int): int

b (int): int

Returns:

a + b

Raises:

AssertionError: if a or b is not integer

"""

assert all([isinstance(a, int), isinstance(b, int)])

return a + b

def test_add():

assert add(1, 2) == 3

assert isinstance(add(1, 2) , int)

with pytest.raises(Exception): # test exception

add('1', 2)

这是个脑残示例,不过基本使用就是这么简单。真实场景下远远比这个复杂,甚至有时候构造测试的时间比写业务逻辑的时间还要长。但是再复杂的逻辑也是一点点功能堆积,如果可以确保每一部分都正确,整体上是不会出错的。单元测试同时也提醒我们,函数完成的功能尽可能单一,这样才利于测试。

下面几个是我常用的pytest命令:

py.test test_mod.py # run tests in module

py.test somepath # run all tests below somepath

py.test -q test_file_name.py # quite输出

py.test -s test_file_name.py # -s参数可以打印测试代码中的输出,默认不打印,print没结果

py.test test_mod.py::test_func # only run tests that match the "node ID",

py.test test_mod.py::TestClass::test_method # run a single method in

测试驱动开发(TDD)的流程

为了实现一个函数,很多人的流程是这样的:

匆匆写代码->实现后print输出看结果->有逻辑或语法错误->修改->继续print输出看结果 循环往复。

采用TDD的流程如下:

TDD三项法则:在编写失败的单元测试之前,不要编写任何产品代码

只要有一个单元测试失败了,就不再写测试代码;

产品代码能够让当前失败的单元测试成功通过即可,不要多写

优点:确定性;大幅减少缺陷;增加重构勇气;单元测试即是文档;改善设计 (事后测试是防守,先行测试是进攻) 当然也不用完全采用tdd,先写测试有时候很繁琐,但是对于比较重要的api函数,最好还是要有单元测试。为了使项目质量得到保证,TDD中的一些思想还是值得借鉴的。很多东西也在摸索,推荐学习下flask,requests等开源项目的单元测试代码,以后再慢慢更新吧。 实际上,如果能把print的结果和预期结果落实到测试代码里,加几个assert语句,就是单元测试了。并且这些测试代码也成为很好的api手册,你看这些测试用例就知道怎么调用了。

TDD实践

之前做新项目的时候(使用了flask cookiecutter生成项目模板),基本上达到了凡是复杂函数或类都会写测试代码的程度。基本上都是用py.test,还是比较方便的。在项目跟目录下建立一个tests文件夹,相关测试代码都放在里边。之后我会装一个用来监控文件变化的命令,我会使用vim的分屏模式同时打开模块代码和其测试代码,然后开个tmux窗口用于边改代码边看测试结果输出,屏幕够宽的话一个屏幕就能搞定。例如:假如我在写一个模块叫做models.py,在tests里写个test_models.py,还有个很简单的shell脚本runtest.sh写上py.test -s test_models.py 首先装一下监控文件变动的命令:

# for ubuntu

sudo pip install https://github.com/joh/when-changed/archive/master.zip

# for mac

brew install fswatch # http://stackoverflow.com/questions/1515730/is-there-a-command-like-watch-or-inotifywait-on-the-mac

然后在tests文件夹下执行:

# ubuntu

when-changed test_models.py ./runtest.sh

# mac

fswatch -o ./*.py | xargs -n1 ./runtest.sh # 比如写单元测试的时候修改后就让测试执行

这样就能非常愉快地边改代码和测试(实际上一定程度上可以说是TDD,只不过我有时候后写测试代码,另外我也在尝试TDD是否真的能够提升代码质量并且不会降低开发效率,凡事只有自己试试才知道,过几个月我再继续更新本片的实践,到时候就知道TDD到底是不是在浪费时间),边看单元测试的执行结果啦:).如果遵守pep8写不超过80列的代码的话,即使用mac air这种小屏幕依然可以改得很爽,效果如下:

如何处理测试中的数据库请求和网络请求?

之前有同学问到如何处理和数据库的交互以及网络请求,结合自己之前写单测的一些经验说一下:处理数据库请求:目前我看到有两种方式。无论使用那种方式,尽量保证数据测试的时候插入,使用完成就销毁。这样换个平台依然很容易构造测试 ,也容易在 CI 系统跑。使用 fixture 类装饰器在一个 TestCase 运行前插入数据到测试数据库。大概就是 fixture 接收一个参数 sql 文件名,然后读取数据插入数据库

在 TestCase 的 setup 里插入数据,在 teardown 里销毁。

@fixture('table.sql')

class SomeTestCase:

pass

class SomeTestCase:

def setUp(self):

# insert value

def tearDown(self):

# destroy value处理外部网络调用。依旧有两种方式stub: 用来处理一些比较通用的请求,比如一个发号器代码

mock: 使用最多的替换掉网络请求的方式,几乎所有场景下都可以用。个人推荐所有网络请求和 rpc 调用等都可以用 mock.patch 来模拟返回值

@registry.stub

class ZoneSeqStub(BaseStub):

def id(self):

return 'zone:///seqd'

@stub('Seq.get_id')

def get_id(self, **kwargs):

return random.randint(1, 100)

class TestCase:

@mock.patch('somemodule.request')

def test_function(self, mock_request):

mock_request.return_value = {} # 构造期望的返回值,我们默认外部调用按照约定是可以工作的,不会对其测试

python测试代码怎么写_Python 单元测试相关推荐

  1. python测试代码怎么写_python unittest编写测试代码

    做开发的朋友在写代码的同时一般都会写测试代码,这对于做运维的同学却很少用. 今天我们就来写写测试代码,用unittest模块. cat test.py import unittest def IsOd ...

  2. python测试代码怎么写_python测量代码运行时间方法

    Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待 4999元包邮 去购买 > Python 社区有句俗语: "python自己带着电池& ...

  3. python测试代码怎么写_如何使用python做单元测试?

    很多编程小白不太理解单元测试,为什么要进行单元测试呢?很简单,主要是提高代码的正确,同时确保重构不出错.接下来我们一起学习怎么用python做单元测试吧. python内置了一个unittest,但是 ...

  4. Python:测试代码

    Python : 测试代码 文章目录 Python : 测试代码 1 测试函数 1.1 单元测试和测试用例 1.2 可通过的测试 1.3 不能通过的测试 1.4 测试未通过时怎么办 1.5 添加新测试 ...

  5. python测试用例怎么写_Python单元测试unittest的具体使用示例

    Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作. unittest是python的标准测试库,相比于其 ...

  6. python图形代码怎么写_【Python3-API】定制化图像接口示例代码

    Python3-urllib3-API定制化图像接口示例代码 AccessToken获取可以参考:http://ai.baidu.com/forum/topic/show/497663(Python3 ...

  7. python测试代码报错:Ran 0 test in 0.00s

    最近在学习selenium时,使用unittest框架进行代码测试时,发现报以下错误: Ran 0 test in 0.00sOK 原因: unittest提供了全局的main()方法,使得一个单元测 ...

  8. 存根类 测试代码 java_常规单元测试和存根–测​​试技术4

    存根类 测试代码 java 我的上一个博客是有关测试代码的方法以及讨论您做什么和不必进行测试的方法的一系列博客中的第三篇. 它基于我使用一种非常常见的模式从数据库检索地址的简单方案: -并且我提出了这 ...

  9. python基础代码的含义_Python基础学习篇

    原标题:Python基础学习篇 1.编码 默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串都是unicode 字符串. 当然你也可以为源码文件指定不同的编码:# -*- codi ...

最新文章

  1. git的简单使用(一些小操作,持续更新)
  2. spring cloud集成Eureka
  3. 三维立体图_原来三维立体图片是这样制作的,学会以后自己也可以设计
  4. opencv-原图基础上添加指定颜色
  5. Oracle数据库之三
  6. 蚂蚁金服 CEO 突然辞职!去向很意外。。。
  7. selenium--自动化识别图片验证码并输入
  8. aspupload 上传组件下载
  9. 商场云WiFi靠谱吗
  10. android课程设计健身,健身软件课程设计.doc
  11. sourcetree教程(去掉注册账号)
  12. Traefik 一个反向代理的新工具
  13. java编译release版本断言_关于Debug和Release之本质区别
  14. Moonlight串流Steam闪屏的小伙伴们,解决方法见本文。
  15. 现代办公室信号干扰解决方案
  16. Metamask添加网络和切换网络
  17. 权限维持——获取登陆账号及安装后门程序
  18. Halo 博客系统部署日记--Docker
  19. 【金猿技术展】慧安金科反洗钱可疑案宗识别技术——自动全方位提取洗钱行为关联信号...
  20. Python之查询天气小程序

热门文章

  1. android notification点击无效,Notification.addAction在Android O中无效
  2. 我的世界服务器修改飞行速度,《我的世界》创造模式飞行速度修改方法介绍
  3. 看你的样子对Vue研究挺深的,我司招Vue,五险一金有兴趣吗?
  4. 皮一皮:谁还不是个孩子...
  5. 教你在微信头像上加皇冠,很漂亮!
  6. 再见!人人影视...
  7. 推荐一款免费的数据库管理工具,比 Navicat 还要好用,功能还很强大
  8. 实战派:一次kafka卡顿事故排查过程!
  9. Spring Security 实战干货:路径Uri中的 Ant 风格
  10. JDK13 GA发布:5大特性解读